---
title: "Moderate user content"
description: "Take reports, hold uploads until a pre-screen clears them, decide with a statement of reasons, take appeals and export the transparency report."
section: "guides"
status: "Building for 0.4"
spec: "moderate"
canonical: "https://mainsdk.dev/docs/guides/moderate-user-content"
updated: "2026-10-28"
---

# Moderate user content

> Take reports, hold uploads until a pre-screen clears them, decide with a statement of reasons, take appeals and export the transparency report.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/moderate-user-content · Updated: 2026-10-28

You will take a report, decide it with a ground, and export the statements for a period. It takes about 20 minutes.

## You need

- A MainSDK app where people post content others see.
- A reviewer role with the `moderate.review` permission.

## 1. Turn it on

```yaml title="main.yaml"
moderate:
  hold: { files: true, media: true, max: 24h }
  reports: { per-user: 20/hour }
  appeals: { window: 6mo }
```

Uploads and media outputs stay held until the pre-screen clears them, and go to a person after 24 hours.

## 2. Take a report

```ts title="app/report.ts"
import { moderate } from "mainsdk/experimental/moderate";

declare const post: { id: string };
declare const user: { id: string };

await moderate.report({ target: { kind: "post", id: post.id }, reason: "harassment", by: user });
```

## 3. Decide it

```ts title="app/review.ts"
import { moderate } from "mainsdk/experimental/moderate";

declare const caseId: string;
declare const reviewer: { id: string };

await moderate.decide(caseId, { action: "remove", ground: { kind: "terms", ref: "terms" }, facts: "Targeted insults", by: reviewer });
```

Any action except `none` needs a ground and facts. The person affected can `moderate.appeal(decisionId, { by })` inside the window, and a different reviewer resolves it with `moderate.resolve`.

## 4. Export the records

```bash
npx mainsdk moderate statements --tenant acme --from 2026-01-01 --to 2026-03-31 --out statements.json
npx mainsdk moderate transparency --tenant acme --from 2026-01-01 --to 2026-03-31 --json
```

`statements` writes the statements of reasons in the `dsa-tdb` format. `transparency` prints counts of reports, decisions and appeals. Both read the tenant's own region.

## Check that it works

- `moderate.statement(decisionId)` returns a statement for the decision you made.
- A second appeal on the same decision is refused.

## Next

- [Moderation reference](https://mainsdk.dev/docs/interfaces/moderate)
- [Show untrusted text safely](https://mainsdk.dev/docs/guides/sanitize-and-protect-inbound)
