Changelog
Changelog entries: drafts, scheduling, publishing and preview links.
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/changelog/draft
What an AI changelog draft would cost and use now: credit cost, credits available, credential source, blocker, shipped posts since the last entry (any member; spends nothing)
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 7 fields
| Name | Type | Required |
|---|---|---|
enabled | boolean | Yes |
source | "platform" | "own_anthropic" | "openrouter" | Yes |
credits_cost | integervalue ≥ 0 | Yes |
credits_available | integervalue ≥ 0 | Yes |
blocker | "ai_disabled" | "ai_unavailable" | "ai_credits_exhausted" | "ai_credential_invalid" | null | Yes |
last_entry_published_at | string (date-time) | null | Yes |
shipped_since_last_entry | integervalue ≥ 0 | Yes |
Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.
curl 'https://api.upvoiced.com/v1/changelog/draft' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/changelog/draft');No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/changelog/draft
AI draft from shipped posts (post_ids, or since the last entry / an ISO time): 5 credits on UpVoiced credits, saved as a draft linked to those posts, never published
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
post_ids | array of string (uuid)items 1–50 | No |
since | "last_entry" | string (date-time) | No |
tone | "friendly" | "neutral" | "technical"default "friendly" | No |
Response 201: 38 fields
| Name | Type | Required |
|---|---|---|
entry | object | Yes |
entry.id | string (uuid) | Yes |
entry.slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
entry.title | stringlength 1–140 | Yes |
entry.body_md | stringlength ≤ 20000 | Yes |
entry.body_html | string | Yes |
entry.cover | object | null | Yes |
entry.cover.id | string (uuid) | Yes |
entry.cover.url | string (uri) | Yes |
entry.version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
entry.categories | array of stringitems ≤ 5 | Yes |
entry.state | "draft" | "scheduled" | "published" | Yes |
entry.scheduled_for | string (date-time) | null | Yes |
entry.published_at | string (date-time) | null | Yes |
entry.author_operator_id | string (uuid) | Yes |
entry.linked_posts | array of objectitems ≤ 50 | Yes |
entry.linked_posts[].id | string (uuid) | Yes |
entry.linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
entry.linked_posts[].title | stringlength 1–140 | Yes |
entry.linked_posts[].board_id | string (uuid) | Yes |
entry.linked_posts[].status | object | Yes |
entry.reaction_counts | object | Yes |
entry.reaction_counts.👍 | integervalue ≥ 0 | Yes |
entry.reaction_counts.❤️ | integervalue ≥ 0 | Yes |
entry.reaction_counts.🎉 | integervalue ≥ 0 | Yes |
entry.created_at | string (date-time) | Yes |
entry.updated_at | string (date-time) | Yes |
credits_used | integervalue ≥ 0 | Yes |
ai_call | object | Yes |
ai_call.id | string (uuid) | Yes |
ai_call.source | "platform" | "own_anthropic" | "openrouter" | Yes |
ai_call.model | string | Yes |
ai_call.prompt_version | string | Yes |
selection | object | Yes |
selection.mode | "post_ids" | "since" | Yes |
selection.since | string (date-time) | null | Yes |
selection.post_count | integervalue 1–50 | Yes |
selection.omitted | 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/changelog/draft' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"post_ids": [
"0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
]
}'const data = await upvoiced.post('/v1/changelog/draft', {
body: { post_ids: ['0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d'] },
});{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "draft_changelog",
"arguments": {
"post_ids": [
"0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
]
}
}
}Tool draft_changelog.
GET/v1/changelog
Changelog entries, newest first, optional state filter, cursor (any member)
| Name | Type | Required |
|---|---|---|
state | "draft" | "scheduled" | "published" | No |
cursor | stringlength 1–512 | No |
limit | integervalue 1–50default 25 | No |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 28 fields
| Name | Type | Required |
|---|---|---|
items | array of objectitems ≤ 50 | Yes |
items[].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 ≤ 20000 | Yes |
items[].body_html | string | Yes |
items[].cover | object | null | Yes |
items[].cover.id | string (uuid) | Yes |
items[].cover.url | string (uri) | Yes |
items[].version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
items[].categories | array of stringitems ≤ 5 | Yes |
items[].state | "draft" | "scheduled" | "published" | Yes |
items[].scheduled_for | string (date-time) | null | Yes |
items[].published_at | string (date-time) | null | Yes |
items[].author_operator_id | string (uuid) | Yes |
items[].linked_posts | array of objectitems ≤ 50 | Yes |
items[].linked_posts[].id | string (uuid) | Yes |
items[].linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
items[].linked_posts[].title | stringlength 1–140 | Yes |
items[].linked_posts[].board_id | string (uuid) | Yes |
items[].linked_posts[].status | object | Yes |
items[].reaction_counts | object | Yes |
items[].reaction_counts.👍 | integervalue ≥ 0 | Yes |
items[].reaction_counts.❤️ | integervalue ≥ 0 | Yes |
items[].reaction_counts.🎉 | integervalue ≥ 0 | Yes |
items[].created_at | string (date-time) | Yes |
items[].updated_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/changelog' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/changelog');{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_changelog",
"arguments": {
"state": "draft"
}
}
}Tool list_changelog.
POST/v1/changelog
Create a draft entry: title, markdown body with media-host images, cover, version, categories, linked posts
| 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 ≤ 20000default "" | No |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | No |
categories | array of stringitems ≤ 5default [] | No |
cover_asset_id | string (uuid) | null | No |
post_ids | array of string (uuid)items ≤ 50default [] | No |
Response 201: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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/changelog' \
-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"
}'const data = await upvoiced.post('/v1/changelog', { body: { title: 'Dark mode' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/changelog/{id}
One entry with linked posts and operator-only reaction counts
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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 'https://api.upvoiced.com/v1/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/changelog/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.
PATCH/v1/changelog/{id}
Edit an entry in any state; editing a published entry purges public caches
| 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 ≤ 20000 | No |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | No |
categories | array of stringitems ≤ 5 | No |
cover_asset_id | string (uuid) | null | No |
post_ids | array of string (uuid)items ≤ 50 | No |
Response 200: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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 PATCH 'https://api.upvoiced.com/v1/changelog/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/changelog/{id}', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { title: 'Dark mode' },
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
DELETE/v1/changelog/{id}
Delete an entry (a published one is purged from public caches)
| 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/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.delete('/v1/changelog/{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/changelog/{id}/publish
Publish now: linked posts move to the done-target status through the status-change path, changelog.published + cache.purge in the same transaction
| 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 |
|---|---|---|
notify | booleandefault true | No |
Response 200: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/publish' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"notify": true
}'const data = await upvoiced.post('/v1/changelog/{id}/publish', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { notify: true },
});{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "publish_changelog",
"arguments": {
"id": "0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d"
}
}
}Tool publish_changelog: without "confirm": true some calls only return a preview.
POST/v1/changelog/{id}/schedule
Schedule publishing between one minute and one year ahead; the worker publishes it exactly once
| 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 |
|---|---|---|
at | string (date-time) | Yes |
notify | booleandefault true | No |
Response 200: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/schedule' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"at": "2026-10-22T12:00:00.000Z"
}'const data = await upvoiced.post('/v1/changelog/{id}/schedule', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: { at: '2026-10-22T12:00:00.000Z' },
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/changelog/{id}/unpublish
Return a published or scheduled entry to draft (published: purges caches)
| 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: 30 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
title | stringlength 1–140 | Yes |
body_md | stringlength ≤ 20000 | Yes |
body_html | string | Yes |
cover | object | null | Yes |
cover.id | string (uuid) | Yes |
cover.url | string (uri) | Yes |
version | string | nullpattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$ | Yes |
categories | array of stringitems ≤ 5 | Yes |
state | "draft" | "scheduled" | "published" | Yes |
scheduled_for | string (date-time) | null | Yes |
published_at | string (date-time) | null | Yes |
author_operator_id | string (uuid) | Yes |
linked_posts | array of objectitems ≤ 50 | Yes |
linked_posts[].id | string (uuid) | Yes |
linked_posts[].slug | stringlength 1–200pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$ | Yes |
linked_posts[].title | stringlength 1–140 | Yes |
linked_posts[].board_id | string (uuid) | Yes |
linked_posts[].status | object | Yes |
linked_posts[].status.id | string (uuid) | Yes |
linked_posts[].status.name | stringlength 1–100 | Yes |
linked_posts[].status.color | stringpattern ^#[0-9a-fA-F]{6}$ | Yes |
linked_posts[].status.category | "open" | "active" | "done" | "closed" | Yes |
reaction_counts | object | Yes |
reaction_counts.👍 | integervalue ≥ 0 | Yes |
reaction_counts.❤️ | integervalue ≥ 0 | Yes |
reaction_counts.🎉 | integervalue ≥ 0 | 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/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/unpublish' \
-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/changelog/{id}/unpublish', {
path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
body: {},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/changelog/{id}/preview-link
Signed, expiring (7 days), noindex preview URL of a draft or scheduled entry; the token is stored hashed
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 2 fields
| Name | Type | Required |
|---|---|---|
url | string (uri) | Yes |
expires_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/changelog/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/preview-link' \
-H "Authorization: Bearer $UPVOICED_API_KEY" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/changelog/{id}/preview-link', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.