Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 280 additions & 37 deletions .speakeasy/gen.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .speakeasy/gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ generation:
generateNewTests: true
skipResponseBodyAssertions: false
cli:
version: 0.4.0
version: 0.5.0
additionalDependencies: {}
agentEnvironmentDetection: true
arrayFlagFormat: repeatable
Expand Down
11,255 changes: 6,646 additions & 4,609 deletions .speakeasy/openapi.json

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,104 @@
# Changelog

## 0.5.0 (2026-10-26)

Full Changelog: [v0.4.0...v0.5.0](https://github.com/moderation-api/cli/compare/v0.4.0...v0.5.0)

### ⚠ Breaking changes

- `GET /authors`: removed the enum value `reportCount` from the `query` request parameter `sortBy`
- `GET /queue/{id}`: the response property `queue/description` became nullable for the status `200`
- `GET /queue/{id}`: removed the required property `queue/filter` from the response with the `200` status
- `GET /queue/{id}/items`: added `subschema #1, subschema #2` to the `data/items/content` response property `anyOf` list for the response status `200`
- `GET /queue/{id}/items`: the response property `data/items/authorId` became nullable for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/contentType` became nullable for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/conversationId` became nullable for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/language` became nullable for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/labels/items/flagged` became optional for the status `200`
- `GET /queue/{id}/items`: the `data/items/content` response's property `type` changed from `string` to `any` for status `200`
- `POST /queue/{id}/items/{itemId}/resolve`: removed the request body
- `POST /queue/{id}/items/{itemId}/unresolve`: removed the request body
- `GET /queue/{id}/stats`: the response property `topReviewers/items/name` became nullable for the status `200`
- `GET /queue/{id}/stats`: the response property `topReviewers/items/userId` became nullable for the status `200`
- `GET /queue/{id}/stats`: removed the required property `actionStats` from the response with the `200` status
- `GET /queue/{id}/stats`: removed the required property `reviewStats/averageTimeToReview` from the response with the `200` status
- `GET /queue/{id}/stats`: removed the required property `topReviewers/items/averageTimePerReview` from the response with the `200` status
- `GET /queue/{id}/stats`: removed the required property `topReviewers/items/topActions` from the response with the `200` status
- `GET /queue/{id}/stats`: removed the required property `trends/flaggedContentTrends` from the response with the `200` status

### API changes

- `GET /actions`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /actions/execute`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /actions/{actionId}/execute`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /actions/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /actions/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /actions/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /auth`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /authors`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /authors/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /authors/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /authors/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /channels/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /channels/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /channels/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /channels/{id}/duplicate`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /projects/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /projects/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /projects/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /projects/{id}/api-keys`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /projects/{id}/duplicate`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /projects/{projectId}/channels`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /projects/{projectId}/channels`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /queue/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /queue/{id}/items`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /queue/{id}/stats`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /webhooks/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /webhooks/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /webhooks/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /wordlist`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /wordlist/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `PUT /wordlist/{id}`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /wordlist/{id}/embedding-status`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `DELETE /wordlist/{id}/words`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `POST /wordlist/{id}/words`: added the new `queue_item_not_found` enum value to the `code` response property for the response status `404`
- `GET /authors`: deleted the `query` request parameter `contentTypes`
- `GET /queue/{id}/items`: deleted the `query` request parameter `sortDirection`
- `GET /queue/{id}/items`: deleted the `query` request parameter `sortField`
- a breaking change was detected but the version is still `1.1.0`
- `GET /analytics/authors`: endpoint added
- `GET /analytics/automation`: endpoint added
- `GET /analytics/channels`: endpoint added
- `GET /analytics/policies`: endpoint added
- `GET /analytics/summary`: endpoint added
- `GET /analytics/volumes`: endpoint added
- `POST /feedback`: endpoint added
- `GET /queue`: endpoint added
- `GET /queue/{id}`: the `queue/resolvedItemsCount` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}`: the `queue/totalItemsCount` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}`: the `queue/unresolvedItemsCount` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}`: added the required property `queue/isDefault` to the response with the `200` status
- `GET /queue/{id}/items`: added the new optional `query` request parameter `sort`
- `GET /queue/{id}/items`: added the optional property `data/items/labels/items/manual` to the response with the `200` status
- `GET /queue/{id}/items`: added the optional property `data/items/labels/items/matches` to the response with the `200` status
- `GET /queue/{id}/items`: added the optional property `data/items/labels/items/shadow_flagged` to the response with the `200` status
- `GET /queue/{id}/items`: removed the optional property `data/items/actions` from the response with the `200` status
- `GET /queue/{id}/items`: the response property `data/items/authorId` became required for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/contentType` became required for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/conversationId` became required for the status `200`
- `GET /queue/{id}/items`: the response property `data/items/language` became required for the status `200`
- `GET /queue/{id}/items`: added the required property `data/items/contentId` to the response with the `200` status
- `GET /queue/{id}/items`: added the required property `data/items/severity` to the response with the `200` status
- `POST /queue/{id}/items/resolve`: endpoint added
- `POST /queue/{id}/items/{itemId}/resolve`: removed the optional property `comment` from the response with the `200` status
- `POST /queue/{id}/items/{itemId}/unresolve`: the `status` response property for the response status `200` was restricted to a list of enum values
- `GET /queue/{id}/stats`: removed the optional property `topReviewers/items/accuracyScore` from the response with the `200` status
- `GET /queue/{id}/stats`: the `reviewStats/totalPending` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}/stats`: the `reviewStats/totalReviewed` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}/stats`: the `topReviewers/items/reviewCount` response's property `type` was narrowed from `number` to `integer` for status `200`
- `GET /queue/{id}/stats`: the `trends/dailyReviewCounts/items/count` response's property `type` was narrowed from `number` to `integer` for status `200`
- `POST /wordlist`: endpoint added

## 0.4.0 (2026-10-23)

Full Changelog: [v0.3.3...v0.4.0](https://github.com/moderation-api/cli/compare/v0.3.3...v0.4.0)
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ modapi content submit --content '{"type":"text","text":"Hey, check out my profil
<!-- Start Custom [guide] -->
## Set up Moderation API in your project

Run `init` in your project's directory. It signs you in if needed, creates a Moderation API project, and writes the project's keys to your env file:
Run `init` in your project's directory. It links one of your Moderation API projects or creates a new one, and writes the project's keys to your env file:

```bash
modapi login # once per machine; opens the browser
Expand All @@ -144,6 +144,8 @@ modapi init --domain acme.com \
--brief "Stop contact details shared outside the platform, scams and harassment"
```

To connect a directory to a project you already have, run `modapi link` (or `modapi link "<project name>"`). It makes a new key for the project, so you can revoke it on its own. Agents and scripts pass `init --project <id or name>` or `init --new` instead of answering the question.

`init` does not change your code. To add the moderation calls, let your coding agent do it with the [setup skill](https://github.com/moderation-api/skills):

```bash
Expand Down Expand Up @@ -362,18 +364,29 @@ modapi projects list --secret-key "$MODAPI_ORGANIZATION_KEY"
<details open>
<summary>Available commands</summary>

* [`analytics`](docs/modapi_analytics.md) - Operations for analytics
* [`summary`](docs/modapi_analytics_summary.md) - Get the analytics summary
* [`policies`](docs/modapi_analytics_policies.md) - Get the most flagged policies
* [`authors`](docs/modapi_analytics_authors.md) - Get the most flagged authors
* [`channels`](docs/modapi_analytics_channels.md) - Get analytics per channel
* [`volumes`](docs/modapi_analytics_volumes.md) - Get volumes over time
* [`automation`](docs/modapi_analytics_automation.md) - Get the automation breakdown
* [`authors`](docs/modapi_authors.md) - Operations for authors
* [`list`](docs/modapi_authors_list.md) - List authors
* [`create`](docs/modapi_authors_create.md) - Create a new author
* [`retrieve`](docs/modapi_authors_retrieve.md) - Get author details
* [`update`](docs/modapi_authors_update.md) - Update author details
* [`delete`](docs/modapi_authors_delete.md) - Delete an author
* [`feedback`](docs/modapi_feedback.md) - Operations for feedback
* [`create`](docs/modapi_feedback_create.md) - Send feedback
* [`queue`](docs/modapi_queue.md) - Operations for queue
* [`list`](docs/modapi_queue_list.md) - List queues
* [`retrieve`](docs/modapi_queue_retrieve.md) - Get a queue
* [`get-stats`](docs/modapi_queue_get-stats.md) - Get queue statistics
* [`items`](docs/modapi_queue_items.md) - Operations for items
* [`list`](docs/modapi_queue_items_list.md) - Get queue items
* [`list`](docs/modapi_queue_items_list.md) - List queue items
* [`resolve`](docs/modapi_queue_items_resolve.md) - Resolve a queue item
* [`resolve-many`](docs/modapi_queue_items_resolve-many.md) - Resolve queue items
* [`unresolve`](docs/modapi_queue_items_unresolve.md) - Unresolve a queue item
* [`actions`](docs/modapi_actions.md) - Operations for actions
* [`list`](docs/modapi_actions_list.md) - List moderation actions
Expand All @@ -394,6 +407,7 @@ modapi projects list --secret-key "$MODAPI_ORGANIZATION_KEY"
* [`retrieve`](docs/modapi_account_retrieve.md) - Get account details
* [`wordlist`](docs/modapi_wordlist.md) - Operations for wordlist
* [`list`](docs/modapi_wordlist_list.md) - List wordlists
* [`create`](docs/modapi_wordlist_create.md) - Create wordlist
* [`retrieve`](docs/modapi_wordlist_retrieve.md) - Get wordlist
* [`update`](docs/modapi_wordlist_update.md) - Update wordlist
* [`get-embedding-status`](docs/modapi_wordlist_get-embedding-status.md) - Get embedding status
Expand Down
84 changes: 84 additions & 0 deletions internal/cli/analytics/authors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.

package analytics

import (
"fmt"
"github.com/moderation-api/cli/internal/client"
"github.com/moderation-api/cli/internal/flagutil"
"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 authorsCmdMeta = []flagutil.FlagMeta{
{FlagName: "range", Shorthand: "r", FieldPath: "Range", Kind: flagutil.FlagKindEnum, Optional: true, HasDefault: true, DefaultStr: "30d", EnumValues: []string{"24h", "7d", "30d", "90d"}, Description: "The time window. '24h' is the last 24 hours, in hourly buckets. '7d', '30d' and '90d' are whole UTC days that end with today, in daily buckets. (options: 24h, 7d, 30d, 90d)"},
{FlagName: "channel", Shorthand: "c", FieldPath: "Channel", Kind: flagutil.FlagKindString, Optional: true, Description: "string value"},
{FlagName: "limit", Shorthand: "l", FieldPath: "Limit", Kind: flagutil.FlagKindInt64, Optional: true, HasDefault: true, DefaultInt: 6, HasMinimum: true, Minimum: 1, HasMaximum: true, Maximum: 50, Description: "integer value"},
{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."},
}

// initAuthorsCmd initializes the authors command.
func initAuthorsCmd(parent *cobra.Command) error {
var cmd = &cobra.Command{
Use: "authors",
Short: "Get the most flagged authors",
Long: "Get the authors with the most flagged content in a time window, largest first.",
Example: " modapi analytics authors",
Args: cobra.NoArgs,
RunE: runAuthorsCmd,
Annotations: map[string]string{
"speakeasy_operation": "getAnalyticsAuthors",
},
}
flagutil.RegisterFlags(cmd, authorsCmdMeta)
if err := flagutil.ValidateMeta[operations.GetAnalyticsAuthorsRequest](authorsCmdMeta); err != nil {
return fmt.Errorf("invalid metadata for authors: %w", err)
}
parent.AddCommand(cmd)
return nil
}

// runAuthorsCmd executes the authors command.
func runAuthorsCmd(cmd *cobra.Command, args []string) error {
if usage.UsageRequested(cmd) {
return usage.EmitSchema(cmd, cmd.OutOrStdout())
}
req, err := flagutil.BuildRequest[operations.GetAnalyticsAuthorsRequest](cmd, authorsCmdMeta, "", "")
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.Analytics.Authors(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
}
Loading
Loading