Skip to content
UpVoiced Docs

API reference

AI settings

AI features, their provider credentials and a connection test.

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 });

Dashboard session only (API keys get 403 session_required):

import { UpVoiced } from '@upvoiced/sdk';

// Dashboard-session operations: the Clerk session token of a signed-in operator.
const upvoiced = new UpVoiced({
  headers: { authorization: `Bearer ${sessionToken}` },
  workspace: workspaceId,
});

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/ai/settings

AI credential source, monthly call cap, connected credentials (fingerprints only) and credits (owner/admin)

Authentication
API key or Dashboard session; API key scope admin
Operation id
getAiSettings
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 17 fields
Response 200 (application/json)
NameTypeRequired
enabledbooleanYes
source"platform" | "own_anthropic" | "openrouter"Yes
monthly_call_capinteger | nullvalue 1–1000000Yes
calls_this_monthintegervalue ≥ 0Yes
platform_availablebooleanYes
credentialsarray of objectYes
credentials[].provider"anthropic" | "openrouter"Yes
credentials[].fingerprintstringpattern ^(sk-ant-|sk-or-|key-)…[A-Za-z0-9_-]{4}$Yes
credentials[].status"active" | "invalid"Yes
credentials[].last_errorstring | nullYes
credentials[].last_validated_atstring (date-time)Yes
credentials[].last_used_atstring (date-time) | nullYes
credentials[].created_atstring (date-time)Yes
creditsobjectYes
credits.balanceintegervalue ≥ 0Yes
credits.availableintegervalue ≥ 0Yes
credits.lowbooleanYes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl 'https://api.upvoiced.com/v1/ai/settings' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.get('/v1/ai/settings');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

PATCH/v1/ai/settings

Choose UpVoiced credits, the own Anthropic key or the OpenRouter connection; set the monthly call cap (owner/admin)

Authentication
API key or Dashboard session; API key scope admin
Operation id
updateAiSettings
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Request body (JSON)
NameTypeRequired
source"platform" | "own_anthropic" | "openrouter"No
monthly_call_capinteger | nullvalue 1–1000000No
Response 200: 17 fields
Response 200 (application/json)
NameTypeRequired
enabledbooleanYes
source"platform" | "own_anthropic" | "openrouter"Yes
monthly_call_capinteger | nullvalue 1–1000000Yes
calls_this_monthintegervalue ≥ 0Yes
platform_availablebooleanYes
credentialsarray of objectYes
credentials[].provider"anthropic" | "openrouter"Yes
credentials[].fingerprintstringpattern ^(sk-ant-|sk-or-|key-)…[A-Za-z0-9_-]{4}$Yes
credentials[].status"active" | "invalid"Yes
credentials[].last_errorstring | nullYes
credentials[].last_validated_atstring (date-time)Yes
credentials[].last_used_atstring (date-time) | nullYes
credentials[].created_atstring (date-time)Yes
creditsobjectYes
credits.balanceintegervalue ≥ 0Yes
credits.availableintegervalue ≥ 0Yes
credits.lowbooleanYes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X PATCH 'https://api.upvoiced.com/v1/ai/settings' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
  "source": "platform"
}'
TypeScript SDK
const data = await upvoiced.patch('/v1/ai/settings', { body: { source: 'platform' } });
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

POST/v1/ai/test

Free connection check through the current credential source (10 per workspace and hour; owner/admin)

Authentication
API key or Dashboard session; API key scope admin
Operation id
testAi
Idempotency
Send an Idempotency-Key to make retries safe.
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
idempotency-keystringpattern ^[A-Za-z0-9_.:-]{8,128}$No
Response 200: 6 fields
Response 200 (application/json)
NameTypeRequired
oktrueYes
source"platform" | "own_anthropic" | "openrouter"Yes
modelstringYes
prompt_versionstringYes
latency_msintegervalue ≥ 0Yes
creditsintegervalue ≥ 0Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X POST 'https://api.upvoiced.com/v1/ai/test' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Idempotency-Key: $(uuidgen)"
TypeScript SDK
const data = await upvoiced.post('/v1/ai/test');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

PUT/v1/ai/credentials/anthropic

Save the workspace’s own Anthropic API key after a validation call; stored encrypted, shown only as a fingerprint (session only)

Authentication
Dashboard session only (API keys get 403 session_required)
Operation id
saveAnthropicCredential
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Request body (JSON)
NameTypeRequired
api_keystringpattern ^sk-ant-[A-Za-z0-9_-]{20,300}$Yes
Response 200: 7 fields
Response 200 (application/json)
NameTypeRequired
provider"anthropic" | "openrouter"Yes
fingerprintstringpattern ^(sk-ant-|sk-or-|key-)…[A-Za-z0-9_-]{4}$Yes
status"active" | "invalid"Yes
last_errorstring | nullYes
last_validated_atstring (date-time)Yes
last_used_atstring (date-time) | nullYes
created_atstring (date-time)Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X PUT 'https://api.upvoiced.com/v1/ai/credentials/anthropic' \
  -H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
  "api_key": "sk-ant-REPLACE_WITH_YOUR_KEY_000000"
}'
TypeScript SDK
const data = await upvoiced.put('/v1/ai/credentials/anthropic', { body: { api_key: 'sk-ant-REPLACE_WITH_YOUR_KEY_000000' } });
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

DELETE/v1/ai/credentials/anthropic

Delete the stored Anthropic key (revoke it in the Anthropic Console too)

Authentication
Dashboard session only (API keys get 403 session_required)
Operation id
deleteAnthropicCredential
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 2 fields
Response 200 (application/json)
NameTypeRequired
oktrueYes
revoke_urlstring (uri)Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X DELETE 'https://api.upvoiced.com/v1/ai/credentials/anthropic' \
  -H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.delete('/v1/ai/credentials/anthropic');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

POST/v1/ai/credentials/openrouter/connect

Start "Connect OpenRouter" (OAuth PKCE S256): the authorize URL to send the browser to (session only)

Authentication
Dashboard session only (API keys get 403 session_required)
Operation id
connectOpenRouter
Idempotency
Send an Idempotency-Key to make retries safe.
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
idempotency-keystringpattern ^[A-Za-z0-9_.:-]{8,128}$No
Response 200: 2 fields
Response 200 (application/json)
NameTypeRequired
authorize_urlstring (uri)Yes
expires_atstring (date-time)Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X POST 'https://api.upvoiced.com/v1/ai/credentials/openrouter/connect' \
  -H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Idempotency-Key: $(uuidgen)"
TypeScript SDK
const data = await upvoiced.post('/v1/ai/credentials/openrouter/connect');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

POST/v1/ai/credentials/openrouter/callback

Finish "Connect OpenRouter": exchange the returned code with the sealed verifier and store the key (session only)

Authentication
Dashboard session only (API keys get 403 session_required)
Operation id
completeOpenRouter
Idempotency
Send an Idempotency-Key to make retries safe.
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
idempotency-keystringpattern ^[A-Za-z0-9_.:-]{8,128}$No
Request body (JSON)
NameTypeRequired
codestringlength 1–512pattern ^[A-Za-z0-9._~-]+$Yes
statestringlength 16–2048pattern ^[A-Za-z0-9_-]+$Yes
Response 200: 7 fields
Response 200 (application/json)
NameTypeRequired
provider"anthropic" | "openrouter"Yes
fingerprintstringpattern ^(sk-ant-|sk-or-|key-)…[A-Za-z0-9_-]{4}$Yes
status"active" | "invalid"Yes
last_errorstring | nullYes
last_validated_atstring (date-time)Yes
last_used_atstring (date-time) | nullYes
created_atstring (date-time)Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X POST 'https://api.upvoiced.com/v1/ai/credentials/openrouter/callback' \
  -H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "acme",
  "state": "feature-requests"
}'
TypeScript SDK
const data = await upvoiced.post('/v1/ai/credentials/openrouter/callback', { body: { code: 'acme', state: 'feature-requests' } });
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

DELETE/v1/ai/credentials/openrouter

Disconnect OpenRouter: delete our copy of the key (revoke it on OpenRouter too)

Authentication
Dashboard session only (API keys get 403 session_required)
Operation id
deleteOpenRouterCredential
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 2 fields
Response 200 (application/json)
NameTypeRequired
oktrueYes
revoke_urlstring (uri)Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X DELETE 'https://api.upvoiced.com/v1/ai/credentials/openrouter' \
  -H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.delete('/v1/ai/credentials/openrouter');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

GET/v1/ai/usage

AI usage: credential source, plan grant window, purchased and held credits, calls by operation (any member; AI-006/BIL-003)

Authentication
API key or Dashboard session; API key scope read
Operation id
getAiUsage
Query parameters
NameTypeRequired
period"this_month" | "last_month" | "last_30_days"default "this_month"No
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 35 fields
Response 200 (application/json)
NameTypeRequired
enabledbooleanYes
source"platform" | "own_anthropic" | "openrouter"Yes
plan"free" | "starter" | "growth" | "business"Yes
grantobjectYes
grant.monthlyintegervalue ≥ 0Yes
grant.window_startstring (date-time) | nullYes
grant.window_endstring (date-time) | nullYes
grant.grantedintegervalue ≥ 0Yes
grant.remainingintegervalue ≥ 0Yes
grant.expires_atstring (date-time) | nullYes
creditsobjectYes
credits.balanceintegervalue ≥ 0Yes
credits.purchasedintegervalue ≥ 0Yes
credits.heldintegervalue ≥ 0Yes
credits.availableintegervalue ≥ 0Yes
credits.lowbooleanYes
credits.exhaustedbooleanYes
can_purchasebooleanYes
periodobjectYes
period.name"this_month" | "last_month" | "last_30_days"Yes
period.fromstring (date-time)Yes
period.tostring (date-time)Yes
totalsobjectYes
totals.callsintegervalue ≥ 0Yes
totals.creditsintegervalue ≥ 0Yes
operationsarray of objectYes
operations[].kindstringpattern ^[a-z][a-z_]{1,39}$Yes
operations[].source"platform" | "own_anthropic" | "openrouter"Yes
operations[].callsintegervalue ≥ 0Yes
operations[].succeededintegervalue ≥ 0Yes
operations[].failedintegervalue ≥ 0Yes
operations[].pendingintegervalue ≥ 0Yes
operations[].creditsintegervalue ≥ 0Yes
operations[].input_tokensintegervalue ≥ 0Yes
operations[].output_tokensintegervalue ≥ 0Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl 'https://api.upvoiced.com/v1/ai/usage' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.get('/v1/ai/usage');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

GET/v1/ai/usage/ledger

AI credit ledger history, newest first, paged by sequence (any member)

Authentication
API key or Dashboard session; API key scope read
Operation id
listAiCreditLedger
Query parameters
NameTypeRequired
beforeintegervalue > 0No
limitintegervalue 1–100default 25No
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 9 fields
Response 200 (application/json)
NameTypeRequired
entriesarray of objectYes
entries[].seqintegervalue > 0Yes
entries[].deltaintegerYes
entries[].reason"plan_grant" | "purchase" | "consume" | "refund" | "adjust" | "expire"Yes
entries[].operationstring | nullpattern ^[a-z][a-z_]{1,39}$Yes
entries[].plan"free" | "starter" | "growth" | "business" | nullYes
entries[].balance_afterintegervalue ≥ 0Yes
entries[].created_atstring (date-time)Yes
next_beforeinteger | nullvalue > 0Yes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl 'https://api.upvoiced.com/v1/ai/usage/ledger' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.get('/v1/ai/usage/ledger');
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

GET/v1/ai/triage

AI triage of up to 100 posts: summary, sentiment and board/tag/duplicate suggestions with their state (any member)

Authentication
API key or Dashboard session; API key scope read
Operation id
listAiTriage
Query parameters
NameTypeRequired
post_idsstringlength ≤ 3700pattern ^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}(,[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}){0,99}$Yes
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
Response 200: 16 fields
Response 200 (application/json)
NameTypeRequired
enabledbooleanYes
itemsarray of objectitems ≤ 100Yes
items[].post_idstring (uuid)Yes
items[].status"pending" | "ready" | "paused" | "failed"Yes
items[].reasonstring | nullYes
items[].summarystring | nulllength ≤ 160Yes
items[].sentiment"positive" | "neutral" | "negative" | "mixed" | nullYes
items[].ai_call_idstring (uuid) | nullYes
items[].updated_atstring (date-time)Yes
items[].suggestionsarray of objectitems ≤ 20Yes
items[].suggestions[].idstring (uuid)Yes
items[].suggestions[].kind"board" | "tag" | "duplicate"Yes
items[].suggestions[].state"open" | "applied" | "dismissed"Yes
items[].suggestions[].confidencenumber | nullvalue 0–1Yes
items[].suggestions[].targetobjectYes
items[].suggestions[].decided_atstring (date-time) | nullYes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl 'https://api.upvoiced.com/v1/ai/triage?post_ids=x' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID"
TypeScript SDK
const data = await upvoiced.get('/v1/ai/triage', { query: { post_ids: 'x' } });
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

POST/v1/ai/suggestions/{id}/apply

Apply one AI suggestion through the normal post update or merge path; audited with its AI call (idempotent)

Authentication
API key or Dashboard session; API key scope write
Operation id
applyAiSuggestion
Idempotency
Send an Idempotency-Key to make retries safe.
Path parameters
NameTypeRequired
idstring (uuid)Yes
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
idempotency-keystringpattern ^[A-Za-z0-9_.:-]{8,128}$No
Response 200: 16 fields
Response 200 (application/json)
NameTypeRequired
post_idstring (uuid)Yes
status"pending" | "ready" | "paused" | "failed"Yes
reasonstring | nullYes
summarystring | nulllength ≤ 160Yes
sentiment"positive" | "neutral" | "negative" | "mixed" | nullYes
ai_call_idstring (uuid) | nullYes
updated_atstring (date-time)Yes
suggestionsarray of objectitems ≤ 20Yes
suggestions[].idstring (uuid)Yes
suggestions[].kind"board" | "tag" | "duplicate"Yes
suggestions[].state"open" | "applied" | "dismissed"Yes
suggestions[].confidencenumber | nullvalue 0–1Yes
suggestions[].targetobjectYes
suggestions[].target.idstring (uuid)Yes
suggestions[].target.labelstringlength 1–300Yes
suggestions[].decided_atstring (date-time) | nullYes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X POST 'https://api.upvoiced.com/v1/ai/suggestions/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/apply' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
TypeScript SDK
const data = await upvoiced.post('/v1/ai/suggestions/{id}/apply', {
  path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
  body: {},
});
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.

POST/v1/ai/suggestions/{id}/dismiss

Dismiss one AI suggestion; remembered, so a re-run never offers it again

Authentication
API key or Dashboard session; API key scope write
Operation id
dismissAiSuggestion
Idempotency
Send an Idempotency-Key to make retries safe.
Path parameters
NameTypeRequired
idstring (uuid)Yes
Headers
NameTypeRequired
x-workspacestring (uuid)Yes
idempotency-keystringpattern ^[A-Za-z0-9_.:-]{8,128}$No
Response 200: 16 fields
Response 200 (application/json)
NameTypeRequired
post_idstring (uuid)Yes
status"pending" | "ready" | "paused" | "failed"Yes
reasonstring | nullYes
summarystring | nulllength ≤ 160Yes
sentiment"positive" | "neutral" | "negative" | "mixed" | nullYes
ai_call_idstring (uuid) | nullYes
updated_atstring (date-time)Yes
suggestionsarray of objectitems ≤ 20Yes
suggestions[].idstring (uuid)Yes
suggestions[].kind"board" | "tag" | "duplicate"Yes
suggestions[].state"open" | "applied" | "dismissed"Yes
suggestions[].confidencenumber | nullvalue 0–1Yes
suggestions[].targetobjectYes
suggestions[].target.idstring (uuid)Yes
suggestions[].target.labelstringlength 1–300Yes
suggestions[].decided_atstring (date-time) | nullYes

Errors: 400, 401, 403, 404, 409, 413, 422, 429, 500, 503, all in the standard error shape.

curl
curl -X POST 'https://api.upvoiced.com/v1/ai/suggestions/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d/dismiss' \
  -H "Authorization: Bearer $UPVOICED_API_KEY" \
  -H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
TypeScript SDK
const data = await upvoiced.post('/v1/ai/suggestions/{id}/dismiss', {
  path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' },
  body: {},
});
MCP

No MCP tool for this operation. The MCP server covers the everyday reads and writes.