DocsDevelopers
MCP

53 read tools, 80 write tools.

Telos exposes curated tools over the Model Context Protocol so external agents can ground decisions in your product state and act on it. Every tool is gated by the calling key's capabilities; every write is idempotent and attributed to the key's user, through the same service layer as REST.

Connect

Endpoint & auth

POST /api/mcp speaks the Streamable HTTP MCP transport. Auth is the same bearer token you use for REST: Authorization: Bearer telos_live_… (or an OAuth app token, telos_oat_…, limited to its granted scopes). Rate limits apply as on REST, and every tool below is gated by the calling key's capabilities — a read-only key sees writes returned as permission errors.

endpoint
https://www.telos-app.com/api/mcp
Quickstart

Connect a client

Three steps to wire Telos into an MCP client. The same endpoint and token work for any client that speaks the Model Context Protocol — pick yours in step 2.

1. Get an API key

Issue a key from inside the app at Configurations → API Keys. Keys start with telos_live_and carry the role that decides which tools and fields you see — copy it now, it's shown only once. The endpoint is the same origin you use Telos on, with /api/mcp appended — there's no per-account host. For this workspace that's https://www.telos-app.com/api/mcp, which is what the snippets below use.

2. Register the server with your client

Select your client. Most speak Streamable HTTP natively, so you give them the endpoint and token directly — only Claude Desktop's config launches local commands, so it bridges through mcp-remote.

Register the endpoint with one CLI command — run it from any project, or add --scope user to make it available everywhere.

terminal
claude mcp add --transport http telos \
  https://www.telos-app.com/api/mcp \
  --header "Authorization: Bearer telos_live_…"

3. Verify the connection

In a client, the Telos tools should now appear in the tool list. To check the endpoint and token directly, send the protocol handshake with curl — a 200 with a result confirms both connectivity and auth; a 401 means the key is missing or invalid:

verify
curl -sN https://www.telos-app.com/api/mcp \
  -H 'Authorization: Bearer telos_live_…' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

Once connected, ask the agent to call whoami to confirm which user and role the key resolves to. The full tool list is below.

Reference

Read tools

Auto-rendered from the source registry. Every input parameter is validated against a Zod schema on the server.

Identity & directory · 1

whoami

Get the identity of the authenticated user.

Input
No parameters.
Vision · 4

list_visions

List strategic visions with cursor pagination: the company vision first, then product visions. Optionally filter by kind (company, product).

Input
  • kind?: "company" | "product"Vision kind filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_vision

Get a vision

Input
  • id: uuidVision UUID

list_vision_versions

Version snapshots of a vision, newest first, with cursor pagination. Every name or narrative edit creates one.

Input
  • id: uuidVision UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

list_vision_objectives

The objectives anchored to this vision, with cursor pagination.

Input
  • id: uuidVision UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Objectives · 3

list_objectives

List strategic objectives with cursor pagination. Optionally scope to a vision.

Input
  • visionId?: uuidVision UUID filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_objective

Get an objective

Input
  • id: uuidObjective UUID

list_objective_opportunities

The opportunities linked to this objective, with their link metadata.

Input
  • objectiveId: uuidObjective UUID
Metrics · 3

list_metrics

List metrics tracked by the organisation, with cursor pagination.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_metric

Get a metric

Input
  • id: uuidMetric UUID

list_metric_values

Recorded values for a metric, ascending by recorded time; when limit caps the count, the newest values are kept. Optionally filter by date (since).

Input
  • metricId: uuidMetric UUID
  • since?: stringOnly values recorded at or after this time
  • limit?: integer | stringMaximum number of values to return
Strategy · 3

list_strategies

List strategies with cursor pagination. Optionally filter by status (draft, active, archived).

Input
  • status?: "draft" | "active" | "archived"Strategy status filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_strategy

Get a strategy

Input
  • id: uuidStrategy UUID

list_strategy_versions

Version snapshots of a strategy, newest first, with cursor pagination. Every content edit creates one.

Input
  • id: uuidStrategy UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Opportunities · 5

list_opportunities

List opportunities with cursor pagination. Optionally filter by lifecycle state or team.

Input
  • state?: "backlog" | "in_progress" | "completed" | "cancelled"Opportunity lifecycle state filter
  • teamId?: uuidTeam UUID filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_opportunity

Get a specific opportunity by id.

Input
  • id: uuidOpportunity UUID

list_opportunity_tasks

The tasks attached to this opportunity, with cursor pagination.

Input
  • id: uuidOpportunity UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_opportunity_context

Get everything needed to act on an opportunity in one call: the opportunity and PRD, linked objectives, the customer(s), comments, linked insights, the active workflow step, and any linked PRs. Sections you lack the read capability for (e.g. customers, insights) come back null. Pass the opportunity ref (e.g. OPP-23).

Input
  • ref: stringOpportunity ref, e.g. OPP-23

get_opportunity_scorecard

Get an opportunity's scorecard: the org's active scoring criteria (name, weight, invert), the shared 1-5 values with attribution, and the computed 0-100 total (null until every criterion is scored).

Input
  • id: uuidOpportunity UUID
Tasks · 5

list_tasks

List tasks with cursor pagination. Optionally filter by opportunity, status, priority, or team.

Input
  • opportunityId?: uuidOpportunity UUID filter
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"Task status filter
  • priority?: "low" | "medium" | "high" | "urgent"Task priority filter
  • teamId?: uuidTeam UUID filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_task

Get a specific task by id.

Input
  • id: uuidTask UUID

get_task_context

Get everything needed to act on a task in one call: the task spec, its parent opportunity and PRD, the customer(s), comments, linked insights, the active workflow step, and any linked PRs. Sections you lack the read capability for (e.g. the parent opportunity, customers, insights) come back null. Pass the task ref (e.g. TF-24).

Input
  • ref: stringTask ref, e.g. TF-24

list_recurring_tasks

List recurring-task definitions with cursor pagination: each one's schedule (human-readable cadence + next occurrence), team, owner, and template. These are the definitions a background sweep materializes into real tasks; use list to see the tasks they produced.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_recurring_task

Get a recurring-task definition by id.

Input
  • id: uuidRecurring-task definition UUID
Insights · 2

list_insights

List customer insights (the customer-voice evidence layer). Optionally filter by lifecycle status or customer.

Input
  • status?: "created" | "review" | "need_context" | "active" | "rejected" | "delivered" | "stale"Insight lifecycle status filter
  • customerId?: uuidCustomer UUID filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_insight

Get an insight with its demand root, links, tags, and committed impacts.

Input
  • id: uuidInsight UUID
Tags · 1

list_tags

List tags (with usage counts) so you can cluster insights onto existing tags instead of minting duplicates.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Customers · 3

list_customers

List customers with cursor pagination. Optionally filter by status.

Input
  • status?: "prospect" | "onboarding" | "pilot" | "live" | "churned" | "lost"Customer status filter
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_customer

Get a customer

Input
  • id: uuidCustomer UUID

list_customer_contacts

List the named people on a customer account, primary contact first, then newest. Use a contact's id to attribute an insight to the person who said it.

Input
  • customerId: uuidCustomer UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Teams · 3

list_teams

List teams in the organisation, with cursor pagination.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_team

Get a team

Input
  • id: uuidTeam UUID

list_team_members

List team members

Input
  • teamId: uuidTeam UUID
Users · 2

list_users

List org members with cursor pagination.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_user

Get a user

Input
  • id: uuidUser UUID
Rooms · 2

list_rooms

List the rooms the authenticated user is a member of (DMs, group channels, system rooms), with unread counts and your per-room state, most recently active first.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_room

Get a room by UUID with your membership role. Members always read; any org user reads an open group channel; a private group, DM, or system room without a membership row is forbidden.

Input
  • id: uuidRoom UUID
Messages · 2

list_messages

List a room's top-level messages, newest first, with cursor pagination (service-capped at 100 per page). Rows carry author, reactions, attachments, and thread reply counts; fetch a thread's replies with the thread action. Requires read access to the room (member, or any org user for an open group channel).

Input
  • roomId: uuidRoom UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

list_message_thread

List the replies threaded under a top-level message, newest first, with cursor pagination. Requires read access to the message's room.

Input
  • id: uuidMessage UUID
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Workflows · 2

list_workflows

List the org's workflow templates with cursor pagination, each with its category label.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_workflow

Get a workflow template with its ordered steps. The terminal Completed/Cancelled pair is system-managed and not included.

Input
  • id: uuidWorkflow UUID
Workflow categories · 2

list_workflow_categories

List the org's workflow categories in display order (used to group workflows and to classify time entries).

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_workflow_category

Get a workflow category

Input
  • id: uuidWorkflow category UUID
PRD templates · 2

list_prd_templates

List the PRD templates visible to the key's user: shared (org-wide) templates plus those owned by the user's teams.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

get_prd_template

Get a PRD template

Input
  • id: uuidPRD template UUID
Time entries · 5

list_time_entries

List time entries in a date range (from/to, max 366 days), newest first, with cursor pagination. Defaults to the authenticated user's own entries. engineerId reads another engineer's entries and requires the salary read permission (compensation:read); a team-scoped grant admits only engineers on the caller's teams.

Input
  • from: stringRange start (YYYY-MM-DD, inclusive)
  • to: stringRange end (YYYY-MM-DD, inclusive; max 366 days after from)
  • engineerId?: uuidAnother engineer's user UUID; requires compensation:read
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

aggregate_time_entries_by_category

Total hours in a date range bucketed by workflow category, across all engineers in the org (optionally filtered to one team's members). Cost reporting: requires an org-wide cost:read grant.

Input
  • from: stringRange start (YYYY-MM-DD, inclusive)
  • to: stringRange end (YYYY-MM-DD, inclusive; max 366 days after from)
  • teamId?: uuidRestrict to one team's members

list_time_entries_for_range

Hydrated time entries for every engineer in a date range (hours, derived cost, engineer, category, target), capped at 5000 rows; `truncated` flags the cap. Cost reporting: requires an org-wide cost:read grant.

Input
  • from: stringRange start (YYYY-MM-DD, inclusive)
  • to: stringRange end (YYYY-MM-DD, inclusive; max 366 days after from)
  • teamId?: uuidRestrict to one team's members

get_actual_cost_for_opportunity

Sum of hours times each entry's snapshotted hourly rate for every time entry logged against the opportunity. Returns 0 when nothing is logged. Requires an org-wide cost:read grant.

Input
  • opportunityId: uuidOpportunity UUID

get_actual_cost_for_task

Sum of hours times each entry's snapshotted hourly rate for every time entry logged against the task. Returns 0 when nothing is logged. Requires an org-wide cost:read grant.

Input
  • taskId: uuidTask UUID
Webhooks · 1

list_webhook_subscriptions

List the org's outbound webhook subscriptions. Secrets are write-once and never returned.

Input
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)
Notifications · 2

list_notifications

List the authenticated user's notifications, newest first, with cursor pagination. Filter to unread only, pinned only, or a set of notification types.

Input
  • unreadOnly?: boolean | "true" | "false"Only return unread notifications
  • pinnedOnly?: boolean | "true" | "false"Only return pinned notifications
  • types?: string[] | stringOnly return these notification types. JSON array on MCP, comma-separated on REST and CLI. One of: strategy.published, strategy.updated, vision.published, customer.update_posted, customer.went_live, insight.assigned, insight.needs_context, insight.rejected, opportunity.completed, opportunity.cancelled, objective.committed_date_breach, workflow.step_assigned, task.handoff, task.agent_run_completed, task.agent_run_failed, task.agent_run_cancelled, comment.mention, comment.thread_reply, doc.mention, room.invited, room.mentioned, community.mentioned, good_vibes.shoutout
  • cursor?: stringOpaque pagination cursor from a previous page's nextCursor
  • limit?: integerItems per page (default 100)

count_unread_notifications

Count the authenticated user's unread notifications.

Input
No parameters.
Reference

Write tools

Each write wraps the same scoped service the REST API uses, so every mutation is typed, validated, audited, and attributed to the key's user — never a system actor. Each tool requires a specific capability; supply an idempotencyKey on any write you might retry so a repeat returns the first result instead of writing again.

create_customer

Create a customer account (name plus optional profile, owner, ARR, and lifecycle status; status defaults to prospect).

Requires
customer:create
Input
  • name: string
  • logoUrl?: string
  • website?: string
  • content?: string
  • status?: "prospect" | "onboarding" | "pilot" | "live" | "churned" | "lost"
  • ownerId?: uuid
  • arr?: string
  • customerSince?: stringISO 8601 date or datetime
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_customer

Update a customer's profile, owner, ARR, health, or lifecycle status. Moving to churned/lost requires a reason (stored as an insight rooted at the customer); moving to onboarding activates the org's default onboarding workflow.

Requires
customer:update
Input
  • name?: string
  • logoUrl?: string | null
  • logoFileId?: uuid | null
  • website?: string | null
  • content?: string
  • status?: "prospect" | "onboarding" | "pilot" | "live" | "churned" | "lost"
  • health?: "healthy" | "at_risk" | "critical"
  • ownerId?: uuid | null
  • arr?: string | null
  • customerSince?: string | null
  • reason?: string
  • id: uuidCustomer UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_customer

Hard-delete an account and its insights. Contacts, events, reviews, updates, and opportunity links go with it; linked opportunities survive.

Requires
customer:delete
Input
  • id: uuidCustomer UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_customer_contact

Add a named person to a customer account (name plus optional title, role, email, phone, and notes). Marking the contact primary demotes the account's existing primary.

Requires
customer:create
Input
  • customerId: uuid
  • name: string
  • title?: string
  • role?: "economic_buyer" | "champion" | "decision_maker" | "technical" | "end_user" | "executive" | "other"
  • email?: string
  • phone?: string
  • notes?: string
  • isPrimary?: boolean
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_customer_contact

Update a contact's details. Promoting a contact to primary demotes the account's existing primary.

Requires
customer:update
Input
  • id: uuidContact UUID
  • customerId: uuidCustomer UUID
  • name?: string
  • title?: string | null
  • role?: "economic_buyer" | "champion" | "decision_maker" | "technical" | "end_user" | "executive" | "other"
  • email?: string | null
  • phone?: string | null
  • notes?: string | null
  • isPrimary?: boolean
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_customer_contact

Remove a contact from a customer account. Insights attributed to the contact survive and fall back to the customer as their demand root.

Requires
customer:delete
Input
  • customerId: uuidCustomer UUID
  • id: uuidContact UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_strategy

Create a strategy (name plus a markdown content body). Starts as a draft.

Requires
strategy:create
Input
  • name: string
  • content: string
  • status?: "draft" | "active" | "archived"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_strategy

Update a strategy's name or content. An optional changeNote labels the version this edit creates. Owner-only; ACL enforced by the service.

Requires
strategy:update
Input
  • name?: string
  • content?: string
  • changeNote?: string
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

publish_strategy

Publish a draft strategy, making it the active version. Owner-only; ACL enforced by the service.

Requires
strategy:create
Input
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

archive_strategy

Archive a strategy, retiring it from the active set. Owner-only; ACL enforced by the service.

Requires
strategy:create
Input
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_vision

Create a vision: the company vision (kind 'company', one per org) or a product vision (kind 'product'). The narrative is the persuasive 2-5 year story, not a slogan.

Requires
vision:create
Input
  • name: string
  • kind?: "company" | "product"
  • narrative: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_vision

Update a vision's name or narrative. Edits are versioned.

Requires
vision:update
Input
  • name?: string
  • narrative?: string
  • id: uuidVision UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_metric

Create a metric (name, unit, optional data source and steward).

Requires
metric:create
Input
  • name: string
  • description?: string
  • currentValue?: number
  • unit: "percentage" | "count" | "currency" | "ratio" | "duration" | "custom"
  • dataSourceType?: "manual" | "integration" | "system"
  • dataSourceConfig?: object | null
  • stewardId?: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_metric

Update a metric's name, description, unit, data source, or steward.

Requires
metric:update
Input
  • name?: string
  • description?: string
  • currentValue?: number
  • unit?: "percentage" | "count" | "currency" | "ratio" | "duration" | "custom"
  • dataSourceType?: "manual" | "integration" | "system"
  • dataSourceConfig?: object | null
  • stewardId?: uuid | null
  • id: uuidMetric UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_metric

Permanently delete a metric.

Requires
metric:delete
Input
  • id: uuidMetric UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

record_metric_value

Record an observed value for a metric. recordedAt defaults to now; pass it to backfill history.

Requires
metric:create
Input
  • metricId: uuid
  • value: number
  • recordedAt?: string
  • note?: string
  • source?: "manual" | "integration" | "system"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_objective

Create a strategic objective under a vision. The objective IS the commitment to a number: it requires a DRI and a metric block (metricId, operator, target).

Requires
objective:create
Input
  • title: string
  • description?: string
  • deadline?: string
  • visionId: uuid
  • driId: uuid
  • metric: object
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_objective

Update an objective's title, description, deadline, DRI, or status.

Requires
objective:update
Input
  • title?: string
  • description?: string
  • deadline?: string | null
  • driId?: uuid
  • status?: "active" | "achieved" | "abandoned"
  • id: uuidObjective UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_objective

Permanently delete an objective.

Requires
objective:delete
Input
  • id: uuidObjective UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

set_objective_metric

Replace which metric an objective points at, and/or change its target shape (operator, target, start value). An objective has exactly one metric.

Requires
objective:create
Input
  • objectiveId: uuid
  • metricId: uuid
  • operator: "gte" | "lte" | "eq"
  • target: number
  • startValue?: number | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_opportunity

Create a work opportunity. The description seeds the PRD (or a PRD template does, when omitted). Optionally set lead, team, workflow, category, initial objective links, and tags. Financial fields are ACL-gated.

Requires
opportunity:create
Input
  • title: string
  • description?: string
  • leadId?: uuid
  • teamId?: uuid | null
  • workflowId?: uuid
  • initialState?: "backlog" | "in_progress"
  • categoryId?: uuid | null
  • objectiveLinks?: object[]
  • tagIds?: string[]
  • dueDate?: string
  • prdTemplateId?: uuid | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_opportunity

Update an opportunity's title, lead, team, or PRD body. Lifecycle moves go through write_handoff (workflow steps) or reject_opportunity, not here.

Requires
opportunity:update
Input
  • title?: string
  • leadId?: uuid | null
  • teamId?: uuid | null
  • categoryId?: uuid | null
  • prd?: string | null
  • id: uuidOpportunity UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_opportunity

Permanently delete an opportunity and its dependency edges. Prefer reject_opportunity to record a won't-build decision; delete is for mistakes, not outcomes.

Requires
opportunity:delete
Input
  • id: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

score_opportunity

Set one scorecard value on an opportunity: a shared 1-5 value for one of the org's scoring criteria (last write wins, attributed to the caller). Use get_opportunity_scorecard for criteria ids and the computed total.

Requires
opportunity:update
Input
  • opportunityId: uuid
  • criterionId: uuid
  • value: integerScore value on the 1-5 scale
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

clear_opportunity_score

Clear one scorecard value on an opportunity (the total returns to null until re-scored).

Requires
opportunity:update
Input
  • opportunityId: uuid
  • criterionId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

link_objective

Tie an opportunity to an objective (membership only: 'this bet claims to matter for that number'). Impact evidence lives on insights, not the link.

Requires
opportunity:create
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_objective_link

Repoint an opportunity's objective link at a different objective.

Requires
opportunity:update
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • patch: object
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unlink_objective

Remove the tie between an opportunity and an objective.

Requires
opportunity:delete
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

reject_opportunity

Reject an opportunity: skips remaining workflow steps, lands it on the Cancelled terminal step (or flips a workflow-less one to cancelled), and records the categorized won't-build decision on the thread.

Requires
opportunity:create
Input
  • opportunityId: uuid
  • category: "no_demand" | "not_aligned" | "not_viable" | "not_now" | "duplicate" | "other"
  • reason: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

post_opportunity_update

Post a status update on an opportunity, authored by the key's user. It lands in the opportunity's updates stream and flows into every linked customer's feed. For a plain discussion comment, use create_comment.

Requires
opportunity:update
Input
  • opportunityId: uuidOpportunity UUID
  • body: stringThe update text
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_task

Create a task, attached to an opportunity (opportunityId) or standalone (bug fixes and maintenance are first-class). Optionally seed subtasks, owner, team, category, workflow, and tags in the same call.

Requires
task:create
Input
  • title: string
  • description?: string
  • content?: string
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • priority?: "low" | "medium" | "high" | "urgent"
  • opportunityId?: uuid
  • teamId?: uuid | null
  • ownerId?: uuid | null
  • categoryId?: uuid | null
  • parentTaskId?: uuid
  • subtaskPosition?: integer
  • subtaskMode?: "standalone" | "sequential"
  • subtasks?: object[]
  • workflowId?: uuid
  • onboardingStepInstanceId?: uuid
  • estimateMinutes?: integer
  • dueDate?: string
  • tagIds?: string[]
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_task

Update a task's fields (title, description, content, priority, opportunity, team, category, subtask mode, estimate, due date). For a bare status move prefer set-status, which also auto-assigns an owner to ownerless work.

Requires
task:update
Input
  • title?: string
  • description?: string
  • content?: string | null
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • priority?: "low" | "medium" | "high" | "urgent"
  • opportunityId?: uuid | null
  • teamId?: uuid | null
  • workflowId?: uuid
  • categoryId?: uuid | null
  • subtaskMode?: "standalone" | "sequential"
  • estimateMinutes?: integer | null
  • dueDate?: string | null
  • id: uuidTask UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_task

Permanently delete a task. Its time entries, tags, checklist items, and insight links go with it, and its dependency edges are cleared; any subtasks are detached and survive as standalone tasks. To retire work without losing the record, set its status to cancelled instead.

Requires
task:delete
Input
  • id: uuidTask UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

set_task_status

Set a task's status. Moving an ownerless task to todo/in_progress assigns the caller as its owner — work in flight is never ownerless.

Requires
task:update
Input
  • id: uuid
  • status: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

write_task_handoff

Post the handoff note for a completed subtask in a sequential task, briefing the next owner. Complete the subtask first (set-status), then write the note; it lands on the parent task's thread and clears the pending-note reminder.

Requires
task:update
Input
  • fromTaskId: uuid
  • note: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_recurring_task

Define a recurring task: an RRULE schedule plus a task template. A background sweep creates one real task for the given team on every occurrence (status todo, due = occurrence + dueOffsetDays). This creates the DEFINITION, not a single task — use create for a one-off. rrule is an iCal RRULE string (e.g. 'FREQ=WEEKLY;BYDAY=MO'); dtstart/tzid default to now/UTC and it never backfills.

Requires
task:create
Input
  • name: stringLabel for the recurring-task definition (not the task title)
  • teamId: uuidTeam the generated tasks belong to
  • rrule: stringiCal RRULE string, e.g. 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYMONTHDAY=1'
  • dtstart?: stringRecurrence start (ISO 8601). Defaults to now
  • tzid?: stringIANA timezone the cadence reads in. Defaults to UTC
  • active?: boolean
  • title: string
  • description?: string
  • content?: string
  • priority?: "low" | "medium" | "high" | "urgent"
  • ownerUserId: uuidRequired. Owns every task this definition generates.
  • categoryId?: uuid | null
  • workflowId?: uuid | null
  • estimateMinutes?: integer | null
  • tagIds?: string[]
  • dueOffsetDays?: integer
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_recurring_task

Update a definition's schedule, template, or active flag. Editing the schedule recomputes forward from now, never retroactively.

Requires
task:update
Input
  • name?: stringLabel for the recurring-task definition (not the task title)
  • teamId?: uuidTeam the generated tasks belong to
  • rrule?: stringiCal RRULE string, e.g. 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYMONTHDAY=1'
  • dtstart?: stringRecurrence start (ISO 8601). Defaults to now
  • tzid?: stringIANA timezone the cadence reads in. Defaults to UTC
  • active?: boolean
  • title?: string
  • description?: string
  • content?: string
  • priority?: "low" | "medium" | "high" | "urgent"
  • ownerUserId?: uuidRequired. Owns every task this definition generates.
  • categoryId?: uuid | null
  • workflowId?: uuid | null
  • estimateMinutes?: integer | null
  • tagIds?: string[]
  • dueOffsetDays?: integer
  • id: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

set_recurring_task_active

Pause or resume a recurring-task definition. Paused definitions stop materializing tasks; resuming never backfills the paused gap.

Requires
task:update
Input
  • id: uuidRecurring-task definition UUID
  • active: boolean | "true" | "false"true to resume, false to pause
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_recurring_task

Delete a recurring-task definition. Tasks it already created stay; no new ones are generated.

Requires
task:delete
Input
  • id: uuidRecurring-task definition UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_team

Create a team. The 2-4 character prefix becomes the team's task ref prefix (e.g. ENG-42).

Requires
team:create
Input
  • name: string
  • description?: string
  • prefix?: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_team

Update a team's name, description, ref prefix, or default PRD template.

Requires
team:update
Input
  • name?: string
  • description?: string
  • defaultPrdTemplateId?: uuid | null
  • prefix?: string
  • id: uuidTeam UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_team

Delete a team. Its opportunities/tasks keep their rows (team unset, refs stable); memberships and team-scoped config are removed. Refuses to delete the org's only team.

Requires
team:delete
Input
  • id: uuidTeam UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

add_team_member

Add a user to a team's roster.

Requires
team:create
Input
  • teamId: uuid
  • userId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

remove_team_member

Remove a user from a team's roster.

Requires
team:delete
Input
  • teamId: uuidTeam UUID
  • userId: uuidUser UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_user

Update an org member's profile (name, weekly hours). Salary fields require a leadership role and travel as a pair; role changes need org:manage, which API keys never carry.

Requires
user:update
Input
  • name?: string
  • salaryMonthly?: number
  • salaryCurrency?: "USD" | "EUR" | "GBP" | "CAD" | "AUD" | "CHF" | "SEK" | "NOK" | "DKK" | "JPY"
  • weeklyHours?: integer
  • role?: "engineer" | "pm" | "em" | "executive" | "admin" | "sales" | "operations"
  • orgRoleId?: uuid | null
  • id: uuidUser UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_tag

Create a tag for clustering insights. List existing tags first (list_tags) and reuse them rather than minting duplicates.

Requires
tag:create
Input
  • name: string
  • themeId?: uuid | null
  • color?: "red" | "teal" | "orange" | "lime" | "green" | "cyan" | "blue" | "indigo" | "purple" | "neutral" | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_comment

Post a comment on a task or opportunity, authored by the key's user. It lands on the entity's discussion thread. For an opportunity status broadcast that flows into linked customers' feeds, use post_opportunity_update instead.

Requires
comment:create
Input
  • entityType: "task" | "opportunity"
  • entityId: uuid
  • body: string
  • kind?: "user" | "update"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_insight

Record a customer insight (one discrete signal, not a whole document). Roots at exactly one demand source: a customer, an internal stakeholder (org user), or an other stakeholder (externalStakeholderId — a named voice with no Telos seat). Optionally set source, moscow priority, deadline, tagIds for clustering, a reviewTeamId to route it into a team's review queue, and attach to an opportunity/task/objective.

Requires
insight:create
Input
  • customerId?: uuid | null
  • stakeholderUserId?: uuid | null
  • externalStakeholderId?: uuid | null
  • contactId?: uuid | null
  • verbatim: string
  • source: "interview" | "support" | "sales" | "async" | "observation" | "submission" | "community"
  • moscow?: "must" | "should" | "could" | "wont" | null
  • deadline?: string | null
  • reviewTeamId?: uuid | null
  • attachToOpportunityId?: uuid
  • attachToTaskId?: uuid
  • attachToObjectiveId?: uuid
  • pinned?: boolean
  • tagIds?: string[]
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_insight

Update an insight's verbatim, source, demand root, contact, moscow priority, or deadline. The service keeps exactly one demand root after the merge.

Requires
insight:update
Input
  • verbatim?: string
  • source?: "interview" | "support" | "sales" | "async" | "observation" | "submission" | "community"
  • customerId?: uuid | null
  • stakeholderUserId?: uuid | null
  • externalStakeholderId?: uuid | null
  • contactId?: uuid | null
  • moscow?: "must" | "should" | "could" | "wont" | null
  • deadline?: string | null
  • id: uuidInsight UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_insight

Permanently delete an insight and its links. Prefer the review lifecycle (reject/archive) to record an outcome; delete is for mistakes.

Requires
insight:delete
Input
  • id: uuidInsight UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

attach_insight

Link an existing insight to an opportunity, task, or objective (optionally pinned).

Requires
insight:update
Input
  • insightId: uuid
  • entityType: "opportunity" | "task" | "objective"
  • entityId: uuid
  • pinned?: boolean
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_prd_template

Create a PRD template (markdown scaffold for opportunity PRDs). teamId scopes it to a team; omit for a shared org-wide template, which requires the prd_template:update capability.

Requires
prd_template:create
Input
  • name: string
  • content: string
  • teamId?: uuid | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_prd_template

Update a PRD template's name, content, or team scope.

Requires
prd_template:update
Input
  • id: uuid
  • name?: string
  • content?: string
  • teamId?: uuid | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_prd_template

Delete a PRD template. Opportunities that already applied it keep their PRD content.

Requires
prd_template:delete
Input
  • id: uuidPRD template UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_webhook_subscription

Subscribe an HTTPS endpoint to outbound events. Deliveries are signed with the secret (t=/v1= scheme); the secret is encrypted at rest and never returned, so store it now.

Requires
webhook:create
Input
  • url: string
  • secret: string
  • eventTypes: string[]
  • description?: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_webhook_subscription

Delete a webhook subscription. Deliveries stop immediately.

Requires
webhook:delete
Input
  • id: uuidWebhook subscription UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_workflow

Create a workflow template. Optionally set a categoryId to group it. Every workflow closes with system-managed Completed and Cancelled steps; add real steps with create-step.

Requires
workflow:create
Input
  • name: string
  • description?: string
  • defaultEntityType?: "task" | "opportunity" | "customer"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_workflow

Update a workflow's name, description, category, or default entity type.

Requires
workflow:update
Input
  • name?: string
  • description?: string | null
  • defaultEntityType?: "task" | "opportunity" | "customer" | null
  • id: uuidWorkflow UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_workflow

Delete a workflow template. Refused while active tasks or opportunities are using it.

Requires
workflow:delete
Input
  • id: uuidWorkflow UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_workflow_step

Append a step to a workflow, ahead of the terminal pair. Owner defaults to the caller when defaultDriUserId is omitted.

Requires
workflow:create
Input
  • workflowId: uuid
  • title: string
  • description?: string
  • defaultDriRole?: "engineer" | "pm" | "em" | "executive" | "admin" | "sales" | "operations"
  • defaultDriUserId?: uuid
  • slaTargetHours?: integer
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_workflow_step

Update a step's title, description, position, owner, or SLA. Terminal steps are system-managed and can't be edited.

Requires
workflow:update
Input
  • position?: integer
  • title?: string
  • description?: string
  • defaultDriRole?: "engineer" | "pm" | "em" | "executive" | "admin" | "sales" | "operations" | null
  • defaultDriUserId?: uuid
  • slaTargetHours?: integer | null
  • id: uuidWorkflow step UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_workflow_step

Delete a step and close the position gap. Refused for terminal steps and for steps active work is currently on.

Requires
workflow:delete
Input
  • id: uuidWorkflow step UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

write_handoff

Move an opportunity or customer-onboarding workflow to a step and post a handoff message for it. Jumps to the target step instance (optionally completing the rest) and records the handoff note on the entity's thread. For a subtask sequence inside a task, use write_task_handoff instead.

Requires
workflow:update
Input
  • targetStepInstanceId: uuid
  • markComplete?: boolean
  • message?: string
  • mentions?: object
  • mentionLinkUrl?: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_workflow_category

Create a workflow category. The slug is derived from the label; color is a theme token or hex value.

Requires
workflow_category:create
Input
  • label: string
  • color: string
  • sortOrder?: integer
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_workflow_category

Update a category's label, color, or sort order. The slug never changes.

Requires
workflow_category:update
Input
  • label?: string
  • color?: string
  • sortOrder?: integer
  • id: uuidWorkflow category UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_workflow_category

Delete a workflow category. The system Bug and Incident categories can't be deleted, nor can a category time entries still reference.

Requires
workflow_category:delete
Input
  • id: uuidWorkflow category UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

mark_notification_read

Mark one of your notifications read.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

mark_all_notifications_read

Mark all of your notifications read.

Requires
none (self-scoped: your own data only)
Input
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

pin_notification

Pin one of your notifications so it stays at the top of the inbox.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unpin_notification

Unpin one of your notifications.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unsubscribe_notification

Unsubscribe from the entity a notification came from, muting its future notifications for you.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_time_entry

Log time for the authenticated user against exactly one target: an opportunity (opportunityId), a task (taskId), or a misc workflow category (miscCategoryId, which requires a note). Hours are 0-24 per entry.

Requires
time_entry:create
Input
  • entryDate: string
  • opportunityId?: uuid | null
  • taskId?: uuid | null
  • miscCategoryId?: uuid | null
  • hours: number
  • note?: string | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_time_entry

Update one of your own time entries (entryDate, hours, note). Only the engineer who logged an entry may edit it.

Requires
time_entry:update
Input
  • entryDate?: string
  • hours?: number
  • note?: string | null
  • id: uuidTime entry UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_time_entry

Delete one of your own time entries. Only the engineer who logged an entry may delete it.

Requires
time_entry:delete
Input
  • id: uuidTime entry UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_room

Create a group channel with at least one other org member; the creator becomes its admin. Visibility 'open' (default) lets any org user read and join; 'private' is invite-only. DMs are started in-app, not here.

Requires
room:create
Input
  • name: string
  • description?: string
  • memberIds: string[]
  • visibility?: "open" | "private"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_room

Rename a group channel or edit its description. Room-admin only; DMs and system rooms cannot be edited.

Requires
room:update
Input
  • name?: string
  • description?: string
  • id: uuidRoom UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

archive_room

Archive a group channel, removing it from everyone's room list. Terminal: rooms have no hard delete. Room-admin only; DMs and system rooms cannot be archived.

Requires
room:update
Input
  • id: uuidRoom UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_message

Post a message to a room you can read, authored by the key's user. Pass parentId (a top-level message UUID) to reply in its thread; threads are two-level. @Name mentions in the body notify the named org members.

Requires
message:create
Input
  • roomId: uuidRoom UUID
  • body: stringMessage body (markdown, max 10000 chars)
  • parentId?: uuidTop-level message UUID to reply to in its thread
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

edit_message

Edit one of your own messages; it is marked edited. Only the author may edit a message, and system messages cannot be edited.

Requires
message:update
Input
  • id: uuidMessage UUID
  • body: stringMessage body (markdown, max 10000 chars)
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_message

Delete one of your own messages (soft delete; attachments are swept). Only the author may delete it on this surface.

Requires
message:delete
Input
  • id: uuidMessage UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.