Moderation
Reports, held comments and blocked end users.
Set up the SDK client used in the examples
API key or Dashboard session:
import { UpVoiced } from '@upvoiced/sdk';
const apiKey = process.env['UPVOICED_API_KEY']; // uv_live_… (Settings → API keys)
const workspace = process.env['UPVOICED_WORKSPACE_ID']; // sent as X-Workspace
if (!apiKey || !workspace) throw new Error('Set UPVOICED_API_KEY and UPVOICED_WORKSPACE_ID.');
const upvoiced = new UpVoiced({ apiKey, workspace });curl examples read $UPVOICED_API_KEY and $UPVOICED_WORKSPACE_IDfrom your shell. MCP examples are the tools/call request an MCP client sends to the MCP server with the same API key.
GET/v1/reports
Moderation queue: end-user reports of posts and comments by status (default open), newest first, with the target excerpt, its author (keyed IP hash, blocked flag) and open report count
| Name | Type | Required |
|---|---|---|
status | "open" | "resolved" | "dismissed"default "open" | No |
cursor | stringlength 1–512 | No |
limit | integervalue 1–100default 25 | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 17 fields
| Name | Type | Required |
|---|---|---|
items | array of object | Yes |
items[].id | string (uuid) | Yes |
items[].target | object | Yes |
items[].target.kind | "post" | "comment" | Yes |
items[].target.post_id | string (uuid) | Yes |
items[].target.comment_id | string (uuid) | null | Yes |
items[].target.excerpt | string | nulllength ≤ 280 | Yes |
items[].target.deleted | boolean | Yes |
items[].target.author | object | Yes |
items[].target.open_reports | integervalue ≥ 0 | Yes |
items[].reason | "spam" | "abuse" | "off_topic" | "other" | Yes |
items[].note | string | nulllength ≤ 500 | Yes |
items[].reporter_end_user_id | string (uuid) | Yes |
items[].status | "open" | "resolved" | "dismissed" | Yes |
items[].created_at | string (date-time) | Yes |
items[].resolved_at | string (date-time) | null | Yes |
next_cursor | string | null | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/reports' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/reports');No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/reports/{id}/resolve
Resolve or dismiss a report: closes every open report of the same post or comment (remove content or block the author with their own routes)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
status | "resolved" | "dismissed" | Yes |
Response 200: 2 fields
| Name | Type | Required |
|---|---|---|
closed | integervalue ≥ 0 | Yes |
status | "resolved" | "dismissed" | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X POST 'https://api.upvoiced.com/v1/reports/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/resolve' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"status": "resolved"
}'const data = await upvoiced.post('/v1/reports/{id}/resolve', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { status: 'resolved' },
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/held-comments
End-user comments held for approval (over the link threshold), newest first; not visible anywhere until approved
| Name | Type | Required |
|---|---|---|
cursor | stringlength 1–512 | No |
limit | integervalue 1–100default 25 | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 15 fields
| Name | Type | Required |
|---|---|---|
items | array of object | Yes |
items[].id | string (uuid) | Yes |
items[].post_id | string (uuid) | Yes |
items[].parent_id | string (uuid) | null | Yes |
items[].body_md | string | Yes |
items[].reason | "links" | Yes |
items[].link_count | integervalue ≥ 0 | Yes |
items[].author | object | Yes |
items[].author.end_user_id | string (uuid) | null | Yes |
items[].author.name | string | nulllength 1–100 | Yes |
items[].author.email | string | nulllength ≤ 254 | Yes |
items[].author.ip_hash | string | nullpattern ^[0-9a-f]{32}$ | Yes |
items[].author.blocked | boolean | Yes |
items[].created_at | string (date-time) | Yes |
next_cursor | string | null | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/held-comments' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/held-comments');No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/held-comments/{id}/approve
Publish a held comment as its author (normal checks, follower notifications from now on)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
Response 200: 14 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
post_id | string (uuid) | Yes |
parent_id | string (uuid) | null | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.id | string (uuid) | null | Yes |
author.name | string | nulllength ≤ 200 | Yes |
body_md | string | Yes |
is_official | boolean | Yes |
pinned | boolean | Yes |
edited_at | string (date-time) | null | Yes |
deleted_at | string (date-time) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X POST 'https://api.upvoiced.com/v1/held-comments/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/approve' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'const data = await upvoiced.post('/v1/held-comments/{id}/approve', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: {},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
DELETE/v1/held-comments/{id}
Reject a held comment: it is deleted and never published
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 1 fields
| Name | Type | Required |
|---|---|---|
ok | true | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X DELETE 'https://api.upvoiced.com/v1/held-comments/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.delete('/v1/held-comments/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/blocks
Block list: end-users, email addresses, email domains and keyed IP hashes refused on every end-user write (owner/admin)
| Name | Type | Required |
|---|---|---|
cursor | stringlength 1–512 | No |
limit | integervalue 1–100default 25 | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 10 fields
| Name | Type | Required |
|---|---|---|
items | array of object | Yes |
items[].id | string (uuid) | Yes |
items[].kind | "end_user" | "email" | "email_domain" | "ip_hash" | Yes |
items[].value | stringlength ≤ 254 | Yes |
items[].end_user_id | string (uuid) | null | Yes |
items[].reason | string | nulllength ≤ 500 | Yes |
items[].expires_at | string (date-time) | null | Yes |
items[].created_by_operator_id | string (uuid) | Yes |
items[].created_at | string (date-time) | Yes |
next_cursor | string | null | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/blocks' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/blocks');No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/blocks
Block an end-user, an email address, an email domain (subdomains included) or a keyed IP hash, optionally until a time; idempotent per subject (owner/admin)
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
Response 201: 8 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
kind | "end_user" | "email" | "email_domain" | "ip_hash" | Yes |
value | stringlength ≤ 254 | Yes |
end_user_id | string (uuid) | null | Yes |
reason | string | nulllength ≤ 500 | Yes |
expires_at | string (date-time) | null | Yes |
created_by_operator_id | string (uuid) | Yes |
created_at | string (date-time) | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X POST 'https://api.upvoiced.com/v1/blocks' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"kind": "end_user",
"end_user_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}'const data = await upvoiced.post('/v1/blocks', {
body: {
kind: 'end_user',
end_user_id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d',
},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
DELETE/v1/blocks/{id}
Remove a block (owner/admin)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 1 fields
| Name | Type | Required |
|---|---|---|
ok | true | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X DELETE 'https://api.upvoiced.com/v1/blocks/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.delete('/v1/blocks/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.