Posts
Feedback posts: list, search, create, update, approve, merge and bulk changes.
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/posts
Inbox: filter by board/status/tag/q (full-text + trigram)/author_type/has_reply/approval/since; sort new/top/trending/updated/relevance (any member)
| Name | Type | Required |
|---|---|---|
board | string (uuid) | No |
status | string (uuid) | No |
tag | string (uuid) | No |
q | stringlength 1–200 | No |
author_type | "end_user" | "operator" | No |
has_reply | boolean | "true" | "false" | No |
approval | "approved" | "pending" | No |
since | string (date-time) | No |
sort | "new" | "top" | "trending" | "updated" | "relevance" | No |
cursor | stringlength 1–512 | No |
limit | integervalue 1–100default 25 | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 39 fields
| Name | Type | Required |
|---|---|---|
items | array of object | Yes |
items[].id | string (uuid) | Yes |
items[].board_id | string (uuid) | Yes |
items[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
items[].title | stringlength 1–140 | Yes |
items[].body_md | stringlength ≤ 10000 | Yes |
items[].status | object | Yes |
items[].status.id | string (uuid) | Yes |
items[].status.name | stringlength 1–100 | Yes |
items[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
items[].status.category | "open" | "active" | "done" | "closed" | Yes |
items[].tags | array of objectitems ≤ 50 | Yes |
items[].tags[].id | string (uuid) | Yes |
items[].tags[].name | stringlength 1–100 | Yes |
items[].tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
items[].images | array of objectitems ≤ 4 | Yes |
items[].images[].id | string (uuid) | Yes |
items[].images[].url | string (uri) | Yes |
items[].images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
items[].images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
items[].author | object | Yes |
items[].author.kind | "operator" | "end_user" | "deleted" | Yes |
items[].author.name | string | nulllength 1–100 | Yes |
items[].vote_count | integervalue ≥ 0 | Yes |
items[].comment_count | integervalue ≥ 0 | Yes |
items[].merged_into_post_id | string (uuid) | null | Yes |
items[].created_at | string (date-time) | Yes |
items[].updated_at | string (date-time) | Yes |
items[].workspace_id | string (uuid) | Yes |
items[].approved_at | string (date-time) | null | Yes |
items[].eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
items[].pinned_comment_id | string (uuid) | null | Yes |
items[].trending_score | numbervalue ≥ 0 | Yes |
items[].follower_count | integervalue ≥ 0 | Yes |
items[].author_end_user_id | string (uuid) | null | Yes |
items[].author_operator_id | string (uuid) | null | Yes |
items[].on_behalf_of_end_user_id | string (uuid) | null | Yes |
items[].last_activity_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/posts' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/posts');{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_posts",
"arguments": {
"board": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool list_posts.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"query": "dark mode"
}
}
}Tool search.
POST/v1/posts
Create a post, optionally on behalf of an end-user by email; posts.active_max (any member)
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000default "" | No |
image_asset_ids | array of string (uuid)items ≤ 4default [] | No |
board_id | string (uuid) | Yes |
status_id | string (uuid) | No |
tag_ids | array of string (uuid)items ≤ 50default [] | No |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | No |
on_behalf_of_email | string (email)length ≤ 254 | No |
Response 201: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_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/posts' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"title": "Dark mode",
"board_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}'const data = await upvoiced.post('/v1/posts', {
body: {
title: 'Dark mode',
board_id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d',
},
});{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_post",
"arguments": {
"title": "Dark mode",
"board_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool create_post.
GET/v1/posts/similar
Up to 5 existing posts similar to a title being typed, optionally on one board (any member)
| Name | Type | Required |
|---|---|---|
q | stringlength ≤ 200 | Yes |
board | string (uuid) | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 12 fields
| Name | Type | Required |
|---|---|---|
items | array of objectitems ≤ 5 | Yes |
items[].id | string (uuid) | Yes |
items[].board_id | string (uuid) | Yes |
items[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
items[].title | stringlength 1–140 | Yes |
items[].status | object | Yes |
items[].status.id | string (uuid) | Yes |
items[].status.name | stringlength 1–100 | Yes |
items[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
items[].status.category | "open" | "active" | "done" | "closed" | Yes |
items[].vote_count | integervalue ≥ 0 | Yes |
items[].score | numbervalue 0–1 | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/posts/similar?q=dark+mode' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/posts/similar', { query: { q: 'dark mode' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/posts/{id}/similar
Up to 5 other posts similar to this post's title (any member)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 12 fields
| Name | Type | Required |
|---|---|---|
items | array of objectitems ≤ 5 | Yes |
items[].id | string (uuid) | Yes |
items[].board_id | string (uuid) | Yes |
items[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
items[].title | stringlength 1–140 | Yes |
items[].status | object | Yes |
items[].status.id | string (uuid) | Yes |
items[].status.name | stringlength 1–100 | Yes |
items[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
items[].status.category | "open" | "active" | "done" | "closed" | Yes |
items[].vote_count | integervalue ≥ 0 | Yes |
items[].score | numbervalue 0–1 | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/similar' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/posts/{id}/similar', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/posts/bulk
All-or-nothing bulk status/tag/untag/merge/delete for up to 100 posts (any member)
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
Response 200: 3 fields
| Name | Type | Required |
|---|---|---|
action | "status" | "tag" | "untag" | "merge" | "delete" | Yes |
count | integervalue ≥ 0 | Yes |
ids | array of string (uuid) | 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/posts/bulk' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"action": "status",
"ids": [
"0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
],
"status_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}'const data = await upvoiced.post('/v1/posts/bulk', {
body: {
action: 'status',
ids: ['0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d'],
status_id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d',
},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/posts/{id}
Get a post, including unapproved and merged ones (any member)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_at | string (date-time) | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/posts/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_post",
"arguments": {
"id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool get_post.
PATCH/v1/posts/{id}
Edit title/body/board/status/tags/images/eta; status changes emit post.status_changed
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
title | stringlength 1–140 | No |
body_md | stringlength ≤ 10000 | No |
image_asset_ids | array of string (uuid)items ≤ 4 | No |
board_id | string (uuid) | No |
status_id | string (uuid) | No |
tag_ids | array of string (uuid)items ≤ 50 | No |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | No |
Response 200: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_at | string (date-time) | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl -X PATCH 'https://api.upvoiced.com/v1/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"title": "Dark mode"
}'const data = await upvoiced.patch('/v1/posts/{id}', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { title: 'Dark mode' },
});{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "update_post",
"arguments": {
"id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool update_post: without "confirm": true some calls only return a preview.
DELETE/v1/posts/{id}
Soft-delete a post (any member; audited)
| 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/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.delete('/v1/posts/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/posts/{id}/approve
Approve a post from the approval queue; idempotent
| 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: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_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/posts/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/posts/{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.
POST/v1/posts/{id}/merge
Merge this post into `into`: votes/followers union, comments moved, undo for 30 days
| 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 |
|---|---|---|
into | string (uuid) | Yes |
Response 200: 10 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
source_post_id | string (uuid) | Yes |
target_post_id | string (uuid) | Yes |
created_at | string (date-time) | Yes |
undo_until | string (date-time) | Yes |
undone_at | string (date-time) | null | Yes |
moved | object | Yes |
moved.votes | integervalue ≥ 0 | Yes |
moved.comments | integervalue ≥ 0 | Yes |
moved.followers | integervalue ≥ 0 | 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/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/merge' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"into": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}'const data = await upvoiced.post('/v1/posts/{id}/merge', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { into: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
});{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "merge_posts",
"arguments": {
"source_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d",
"target_id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool merge_posts: without "confirm": true some calls only return a preview.
POST/v1/posts/{id}/unmerge
Undo the latest merge of this post within 30 days, restoring the snapshot exactly
| 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: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_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/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/unmerge' \
-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/posts/{id}/unmerge', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: {},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/posts/{id}/unapprove
Hide a post: back into the approval queue, out of every public read until a member approves it again; idempotent
| 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: 37 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
board_id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 10000 | Yes |
status | object | Yes |
status.id | string (uuid) | Yes |
status.name | stringlength 1–100 | Yes |
status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
status.category | "open" | "active" | "done" | "closed" | Yes |
tags | array of objectitems ≤ 50 | Yes |
tags[].id | string (uuid) | Yes |
tags[].name | stringlength 1–100 | Yes |
tags[].color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
images | array of objectitems ≤ 4 | Yes |
images[].id | string (uuid) | Yes |
images[].url | string (uri) | Yes |
images[].mime | "image/png" | "image/jpeg" | "image/webp" | Yes |
images[].bytes | integervalue ≤ 5242880value > 0 | Yes |
author | object | Yes |
author.kind | "operator" | "end_user" | "deleted" | Yes |
author.name | string | nulllength 1–100 | Yes |
vote_count | integervalue ≥ 0 | Yes |
comment_count | integervalue ≥ 0 | Yes |
merged_into_post_id | string (uuid) | null | Yes |
created_at | string (date-time) | Yes |
updated_at | string (date-time) | Yes |
workspace_id | string (uuid) | Yes |
approved_at | string (date-time) | null | Yes |
eta_quarter | string | nullpattern ^20[0-9]{2}-Q[1-4]$ | Yes |
pinned_comment_id | string (uuid) | null | Yes |
trending_score | numbervalue ≥ 0 | Yes |
follower_count | integervalue ≥ 0 | Yes |
author_end_user_id | string (uuid) | null | Yes |
author_operator_id | string (uuid) | null | Yes |
on_behalf_of_end_user_id | string (uuid) | null | Yes |
last_activity_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/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/unapprove' \
-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/posts/{id}/unapprove', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: {},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/posts/{id}/ai-triage
Run AI triage for an approved post now (1 credit on UpVoiced credits; 60 per workspace and hour)
| 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: 16 fields
| Name | Type | Required |
|---|---|---|
post_id | string (uuid) | Yes |
status | "pending" | "ready" | "paused" | "failed" | Yes |
reason | string | null | Yes |
summary | string | nulllength ≤ 160 | Yes |
sentiment | "positive" | "neutral" | "negative" | "mixed" | null | Yes |
ai_call_id | string (uuid) | null | Yes |
updated_at | string (date-time) | Yes |
suggestions | array of objectitems ≤ 20 | Yes |
suggestions[].id | string (uuid) | Yes |
suggestions[].kind | "board" | "tag" | "duplicate" | Yes |
suggestions[].state | "open" | "applied" | "dismissed" | Yes |
suggestions[].confidence | number | nullvalue 0–1 | Yes |
suggestions[].target | object | Yes |
suggestions[].target.id | string (uuid) | Yes |
suggestions[].target.label | stringlength 1–300 | Yes |
suggestions[].decided_at | string (date-time) | null | 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/posts/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/ai-triage' \
-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/posts/{id}/ai-triage', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: {},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.