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
12 changes: 6 additions & 6 deletions .speakeasy/gen.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
lockVersion: 2.0.0
id: 2331670b-5789-4489-85ed-fb316d2d6d7e
management:
docChecksum: c27787b45fc5b72aafff31ba945a9e61
docChecksum: 38730339aa1da77855e86c169896f12f
docVersion: 1.1.0
speakeasyVersion: internal
generationVersion: internal
releaseVersion: 5.1.0
configChecksum: 0fc8e088f19e83ce192d82bee6690fae
releaseVersion: 5.1.1
configChecksum: 6cbac05d92e51b9e4f0b80ec196fa8ef
repoURL: https://github.com/moderation-api/sdk-typescript
installationURL: https://github.com/moderation-api/sdk-typescript
published: true
Expand Down Expand Up @@ -690,9 +690,9 @@ trackedFiles:
examples/package.json:
last_write_checksum: sha1:72d702bad23c84698d27164ca08d5f0ff931af65
jsr.json:
last_write_checksum: sha1:b23ce6c990b2ad0091b304725048ea11b9fc9738
last_write_checksum: sha1:d9f5d71a49c0d43a9668b8caa46a1526aacba781
package.json:
last_write_checksum: sha1:8fe6185a672bc6314cb6caa2982f4bab802022a0
last_write_checksum: sha1:f8d16c8a36dbd37faf95aa8e2987a3057a82df41
src/core.ts:
last_write_checksum: sha1:72b4c09e9228c63a9cc3335baa0ae5be47c2bc9c
src/funcs/account-retrieve.ts:
Expand Down Expand Up @@ -790,7 +790,7 @@ trackedFiles:
src/lib/base64.ts:
last_write_checksum: sha1:8c94164f2f78e6041c853186fa01f659591f5913
src/lib/config.ts:
last_write_checksum: sha1:5073bebb0237180a976797fb24284028ae846208
last_write_checksum: sha1:16d4a4e113b0da24d571561048a490241f7d38b7
src/lib/encodings.ts:
last_write_checksum: sha1:4481b2a380f8a16856ef92c66fb370299f56d135
src/lib/env.ts:
Expand Down
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
typescript:
version: 5.1.0
version: 5.1.1
acceptHeaderEnum: false
additionalDependencies:
dependencies: {}
Expand Down
2 changes: 1 addition & 1 deletion .speakeasy/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"servers": [
{
"url": "https://moderationapi.com/api/v1"
"url": "https://api.moderationapi.com/v1"
}
],
"tags": [
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 5.1.1 (2026-10-22)

Full Changelog: [v5.1.0...v5.1.1](https://github.com/moderation-api/sdk-typescript/compare/v5.1.0...v5.1.1)

### Chores

- Update the generated code.

## 5.1.0 (2026-10-22)

Full Changelog: [v5.0.0...v5.1.0](https://github.com/moderation-api/sdk-typescript/compare/v5.0.0...v5.1.0)
Expand Down
131 changes: 131 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Migrating from 4.x to 5.x

Version 5 is generated from the OpenAPI spec with a new generator. Most calls keep their names. Check each item below before you upgrade.

```bash
npm install @moderation-api/sdk@5
```

## Client

- The client class is `ModerationApi` (was `ModerationAPI`). It is also the default export, so `import ModerationApi from "@moderation-api/sdk"` keeps working.
- The client reads the API key from `MODAPI_SECRET_KEY`. To set it in code, pass `secretKey`.

## Imports

The `@moderation-api/sdk/resources` import path is removed. Import types from:

| Types | Import path |
| --- | --- |
| Requests and responses | `@moderation-api/sdk/models/operations` |
| Shared types, for example `PublicAuthor` | `@moderation-api/sdk/models` |
| Error classes | `@moderation-api/sdk/models/errors` |
| Webhook events, `validateWebhook` | `@moderation-api/sdk` |

## Renamed and removed methods

| 4.x | 5.x |
| --- | --- |
| `account.list()` | `account.retrieve()` |
| `actions.execute.execute(params)` | `actions.execute(params)` |
| `actions.execute.executeByID(id, params)` | `actions.execute({ actionId, ... })` or `actions.execute({ actionKey, ... })` |
| `webhooks.constructEvent(rawBody, signature, secret)` | `await validateWebhook({ request, secret })` |
| `auth.*` | Removed |
| `content.stream` | Removed. See [Real-time voice](https://docs.moderationapi.com/content-moderation/real-time-voice). |

## Webhooks

`webhooks.constructEvent` was synchronous and took the raw body and the signature. `validateWebhook` is async, is exported from the package root and takes the whole request: a Fetch `Request`, or a Node.js request (Express, Next.js Pages Router). It reads the secret from `MODAPI_WEBHOOK_SECRET` when you do not pass `secret`.

```typescript
// 4.x
const event = client.webhooks.constructEvent(rawBody, signature, secret);

// 5.x
import { validateWebhook } from "@moderation-api/sdk";
const event = await validateWebhook({ request: req });
```

Webhook payloads keep the API's snake_case field names (`flagged_fields`, `external_id`). Only the client's requests and responses use camelCase.

## camelCase fields

Request and response fields are camelCase. The 4.x types used the API's snake_case names. For example, `authors.create` and `authors.update`:

| 4.x | 5.x |
| --- | --- |
| `external_id` | `externalId` |
| `external_link` | `externalLink` |
| `profile_picture` | `profilePicture` |
| `first_seen` | `firstSeen` |
| `last_seen` | `lastSeen` |
| `manual_trust_level` | `manualTrustLevel` |
| `metadata.email_verified` | `metadata.emailVerified` |
| `metadata.phone_verified` | `metadata.phoneVerified` |
| `metadata.identity_verified` | `metadata.identityVerified` |
| `metadata.is_paying_customer` | `metadata.isPayingCustomer` |

The same applies to responses, for example `author.trustLevel`, `result.evaluation.flagProbability` and `result.recommendation.reasonCodes`.

## Lists

List methods take `page` and `pageSize`, and return `{ data, pagination }` instead of a plain array or a resource-specific shape. You can also iterate over the result to get every page.

## Errors

There is one error class per HTTP status (`BadRequestError`, `NotFoundError`, `RateLimitError`, ...). `code` is the specific error code (for example `invalid_api_key`), not the HTTP category. The old `data` field is removed.

## Organization keys

Organization keys work only with `projects` and `channels`. Project keys work with everything else. Use one client per key.

## Renamed types

| 4.x (`@moderation-api/sdk/resources`) | 5.x |
| --- | --- |
| `AccountListResponse` | `GetAccountResponse` |
| `ActionCreateParams` | `CreateActionRequestBody` |
| `ActionCreateResponse` | `CreateActionResponse` |
| `ActionRetrieveResponse` | `GetActionResponse` |
| `ActionUpdateParams` | `UpdateActionRequestBody` |
| `ActionUpdateResponse` | `UpdateActionResponse` |
| `ActionListParams` | `ListActionsRequest` |
| `ActionListResponse` | `ListActionsResponse` |
| `ActionDeleteResponse` | `DeleteActionResponse` |
| `ExecuteExecuteParams` | `ExecuteActionRequestBody` |
| `ExecuteExecuteResponse` | `ExecuteActionResponse` |
| `AuthorCreateParams` | `CreateAuthorRequestBody` |
| `AuthorUpdateParams` | `UpdateAuthorRequestBody` |
| `AuthorListParams` | `ListAuthorsRequest` |
| `AuthorCreateResponse`, `AuthorRetrieveResponse`, `AuthorUpdateResponse` | `PublicAuthor` (`@moderation-api/sdk/models`) |
| `AuthorListResponse` | `ListAuthorsResponse` |
| `AuthorDeleteResponse` | `DeleteAuthorResponse` |
| `ContentSubmitParams` | `SubmitContentRequestBody` |
| `ContentSubmitResponse` | `SubmitContentResponse` |
| `ItemListParams` | `ListQueueItemsRequest` |
| `ItemListResponse` | `ListQueueItemsResponse` |
| `ItemResolveParams` | `ResolveQueueItemRequestBody` |
| `ItemResolveResponse` | `ResolveQueueItemResponse` |
| `ItemUnresolveParams` | `UnresolveQueueItemRequestBody` |
| `ItemUnresolveResponse` | `UnresolveQueueItemResponse` |
| `QueueRetrieveResponse` | `GetQueueResponse` |
| `QueueGetStatsParams` | `GetQueueStatsRequest` |
| `QueueGetStatsResponse` | `GetQueueStatsResponse` |
| `WebhookEvent` | `WebhookEvent` (`@moderation-api/sdk`) |
| `WebhookSecretRetrieveResponse` | `GetWebhookSecretResponse` |
| `WebhookCreateParams` | `CreateWebhookRequestBody` |
| `WebhookCreateResponse` | `CreateWebhookResponse` |
| `WebhookRetrieveResponse` | `GetWebhookResponse` |
| `WebhookUpdateParams` | `UpdateWebhookRequestBody` |
| `WebhookUpdateResponse` | `UpdateWebhookResponse` |
| `WebhookListResponse` | `ListWebhooksResponse` |
| `WebhookDeleteResponse` | `DeleteWebhookResponse` |
| `WordlistRetrieveResponse` | `GetWordlistResponse` |
| `WordlistUpdateParams` | `UpdateWordlistRequestBody` |
| `WordlistUpdateResponse` | `UpdateWordlistResponse` |
| `WordlistListResponse` | `ListWordlistsResponse` |
| `WordlistGetEmbeddingStatusResponse` | `GetWordlistEmbeddingStatusResponse` |
| `WordAddParams` | `AddWordlistWordsRequestBody` |
| `WordAddResponse` | `AddWordlistWordsResponse` |
| `WordRemoveParams` | `RemoveWordlistWordsRequest` |
| `WordRemoveResponse` | `RemoveWordlistWordsResponse` |
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,27 @@ switch (result.recommendation.action) {
case "reject":
// Block the content
break;
case "review":
// Publish or hold it, and send it to a review queue
break;
case "allow":
// Publish the content
break;
case "review":
default:
// Publish or hold it, and send it to a review queue.
// `default` also catches actions added to the API later.
break;
}

for (const policy of result.policies) {
console.log(`${policy.id}: flagged=${policy.flagged}`);
if ("id" in policy) {
console.log(`${policy.id}: flagged=${policy.flagged}`);
}
}
```

### Values added later

Some fields are open enums, for example `recommendation.action` and the error `code`. The API can add a value, and an older SDK version then returns it as a plain string. It does not throw. Always handle unknown values: for `recommendation.action`, treat an unknown value as `review`, as the `default` branch above does.

## Verifying webhooks

Moderation API signs every webhook delivery with your project's webhook signing secret. The signature is in the `modapi-signature` header.
Expand Down Expand Up @@ -266,6 +274,36 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}
```

### Handle an event

`validateWebhook` returns a `WebhookEvent`, a union of the event types. Switch on `event.type` and TypeScript knows the shape of `event.data.object`. Each event also has its own type, for example `QueueItemRejectedEvent` or `AuthorBlockedEvent`.

Webhook payloads use the API's field names, in snake_case (`flagged_fields`, `external_id`). The request and response objects of the client methods use camelCase (`flaggedFields`, `externalId`).

The payload is not validated against a schema, so a field or an event type added to the API later never fails a delivery. An event type that this SDK version does not know reaches the `default` branch. Read it as `UnknownWebhookEvent`.

```typescript
import { validateWebhook, type UnknownWebhookEvent } from "@moderation-api/sdk";

const event = await validateWebhook({ request });

switch (event.type) {
case "queue_item.rejected": {
const { item, author } = event.data.object;
console.log(item?.id, item?.flagged_fields, author?.external_id);
break;
}
case "author.blocked": {
console.log(event.data.object.author.external_id);
break;
}
default: {
const unknown = event as UnknownWebhookEvent;
console.log(`Unhandled event ${unknown.type}`);
}
}
```
<!-- End Custom [guide] -->


Expand Down Expand Up @@ -674,7 +712,7 @@ The default server can be overridden globally by passing a URL to the `serverURL
import { ModerationApi } from "@moderation-api/sdk";

const moderationApi = new ModerationApi({
serverURL: "https://moderationapi.com/api/v1",
serverURL: "https://api.moderationapi.com/v1",
secretKey: process.env["MODAPI_SECRET_KEY"] ?? "",
});

Expand Down Expand Up @@ -776,13 +814,16 @@ import ModerationApi from "@moderation-api/sdk";
import { ModerationApi } from "@moderation-api/sdk";
```

Request and response types are in `@moderation-api/sdk/models/operations`, shared types in `@moderation-api/sdk/models`, error classes in `@moderation-api/sdk/models/errors` and webhook events in `@moderation-api/sdk/models/webhooks`:
Request and response types are in `@moderation-api/sdk/models/operations`, shared types in `@moderation-api/sdk/models`, error classes in `@moderation-api/sdk/models/errors`. Webhook event types (`WebhookEvent`, `QueueItemRejectedEvent`, ...) are exported from the package root, next to `validateWebhook`:

```typescript
import type { SubmitContentRequest, SubmitContentResponse } from "@moderation-api/sdk/models/operations";
import type { SubmitContentRequestBody, SubmitContentResponse } from "@moderation-api/sdk/models/operations";
import { PermissionDeniedError } from "@moderation-api/sdk/models/errors";
import type { WebhookEvent } from "@moderation-api/sdk";
```

Upgrading from 4.x? Read [MIGRATION.md](MIGRATION.md).

## Timeouts

Set a timeout in milliseconds for all requests on the client, or for one request:
Expand Down
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{
"name": "@moderation-api/sdk",
"version": "5.1.0",
"version": "5.1.1",
"exports": {
".": "./src/index.ts",
"./models/errors": "./src/models/errors/index.ts",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@moderation-api/sdk",
"version": "5.1.0",
"version": "5.1.1",
"author": "Moderation API",
"license": "Apache-2.0",
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Params, pathToFunc } from "./url.js";
* Contains the list of servers available to the SDK
*/
export const ServerList = [
"https://moderationapi.com/api/v1",
"https://api.moderationapi.com/v1",
] as const;

export type SDKOptions = {
Expand Down Expand Up @@ -58,8 +58,8 @@ export function serverURLFromOptions(options: SDKOptions): URL | null {
export const SDK_METADATA = {
language: "typescript",
openapiDocVersion: "1.1.0",
sdkVersion: "5.1.0",
sdkVersion: "5.1.1",
genVersion: "internal",
userAgent:
"speakeasy-sdk/typescript 5.1.0 internal 1.1.0 @moderation-api/sdk",
"speakeasy-sdk/typescript 5.1.1 internal 1.1.0 @moderation-api/sdk",
} as const;
Loading
Loading