Connected apps (OAuth)
The dashboard side of MCP connector sign-in: the consent screen and the OAuth grants an operator can review and disconnect.
Set up the SDK client used in the examples
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.
POST/v1/oauth/consent/preview
MCP OAuth consent screen data: the client, its redirect host and the workspaces the caller administers
| Name | Type | Required |
|---|---|---|
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
request | stringpattern ^[A-Za-z0-9_-]{40,8192}$ | Yes |
Response 200: 20 fields
| Name | Type | Required |
|---|---|---|
client | object | Yes |
client.id | string | Yes |
client.name | string | Yes |
client.kind | "metadata_document" | "registered" | Yes |
client.host | string | null | Yes |
client.uri | string | null | Yes |
redirect | object | Yes |
redirect.uri | string | Yes |
redirect.host | string | Yes |
redirect.kind | "web" | "loopback" | "app" | Yes |
requested_scopes | array of "read" | "write" | Yes |
resource | string | Yes |
expires_at | string (date-time) | Yes |
workspaces | array of object | Yes |
workspaces[].id | string (uuid) | Yes |
workspaces[].name | string | Yes |
workspaces[].slug | string | Yes |
workspaces[].role | "owner" | "admin" | Yes |
workspaces[].mcp_enabled | boolean | Yes |
workspaces[].write_allowed | boolean | 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/oauth/consent/preview' \
-H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"request": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}'const data = await upvoiced.post('/v1/oauth/consent/preview', {
body: {
request: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/oauth/consent
Approve an MCP OAuth connection for X-Workspace with read or write access (owner/admin)
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
request | stringpattern ^[A-Za-z0-9_-]{40,8192}$ | Yes |
scope | "read" | "write" | Yes |
Response 200: 1 fields
| Name | Type | Required |
|---|---|---|
redirect_to | string | 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/oauth/consent' \
-H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"request": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"scope": "read"
}'const data = await upvoiced.post('/v1/oauth/consent', {
body: {
request: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
scope: 'read',
},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
POST/v1/oauth/consent/deny
Decline an MCP OAuth connection (back to the app with access_denied)
| Name | Type | Required |
|---|---|---|
idempotency-key | stringpattern ^[A-Za-z0-9_.:-]{8,128}$ | No |
| Name | Type | Required |
|---|---|---|
request | stringpattern ^[A-Za-z0-9_-]{40,8192}$ | Yes |
Response 200: 1 fields
| Name | Type | Required |
|---|---|---|
redirect_to | string | 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/oauth/consent/deny' \
-H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"request": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}'const data = await upvoiced.post('/v1/oauth/consent/deny', {
body: {
request: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
},
});No MCP tool for this operation. The MCP server covers the everyday reads and writes.
GET/v1/oauth/grants
List the workspace's connected MCP apps (OAuth grants; owner/admin)
| 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[].client_id | string | Yes |
items[].client_name | string | Yes |
items[].client_kind | "metadata_document" | "registered" | Yes |
items[].redirect_host | string | Yes |
items[].scopes | array of "read" | "write"items 1–2 | Yes |
items[].created_by | object | Yes |
items[].created_by.id | string (uuid) | Yes |
items[].created_by.name | string | Yes |
items[].created_at | string (date-time) | Yes |
items[].expires_at | string (date-time) | Yes |
items[].last_used_at | string (date-time) | null | Yes |
items[].revoked_at | string (date-time) | null | Yes |
next_cursor | 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/oauth/grants' \
-H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.get('/v1/oauth/grants');No MCP tool for this operation. The MCP server covers the everyday reads and writes.
DELETE/v1/oauth/grants/{id}
Disconnect an MCP app: revoke its OAuth grant and tokens (effective on the next request)
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
| Name | Type | Required |
|---|---|---|
x-workspace | string (uuid) | Yes |
Response 200: 13 fields
| Name | Type | Required |
|---|---|---|
id | string (uuid) | Yes |
client_id | string | Yes |
client_name | string | Yes |
client_kind | "metadata_document" | "registered" | Yes |
redirect_host | string | Yes |
scopes | array of "read" | "write"items 1–2 | Yes |
created_by | object | Yes |
created_by.id | string (uuid) | Yes |
created_by.name | string | Yes |
created_at | string (date-time) | Yes |
expires_at | string (date-time) | Yes |
last_used_at | string (date-time) | null | Yes |
revoked_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 DELETE 'https://api.upvoiced.com/v1/oauth/grants/0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' \
-H "Authorization: Bearer $UPVOICED_SESSION_TOKEN" \
-H "X-Workspace: $UPVOICED_WORKSPACE_ID"const data = await upvoiced.delete('/v1/oauth/grants/{id}', { path: { id: '0190f5e2-4c1d-7a3b-8c2d-5e6f7a8b9c0d' } });No MCP tool for this operation. The MCP server covers the everyday reads and writes.