MCP server
The Jotly MCP integration connects your AI tools straight to your workspace through a remote MCP server (streamable HTTP, stateless) with 82 tools: the same operations as the REST API, except file uploads and erasing personal data, which are REST only.
If your editor or assistant supports remote MCP servers, you can connect it to Jotly without installing any local package. Your agents triage the inbox, vote on behalf of your customers and write changelog entries.
Server URL
Jotly exposes a single remote MCP endpoint:
https://jotly.si/api/v1/mcpIt's a remote MCP server over HTTP, so your client needs to support remote connections and custom headers.
Authentication
Authenticate requests with a Jotly API key:
Authorization: Bearer pk_live_YOUR_KEYCreate the key in Settings → API. It's the same one the REST API uses. Listing tools doesn't require a key; calling them does.
When to use MCP
Use the MCP server when you want an AI assistant to work with Jotly from a compatible client like Claude Code, Cursor or Codex.
MCP is a good fit when you want to:
- bring Jotly context into an AI workflow
- let an assistant review your workspace data without building your own integration
- connect Jotly to a coding or research tool that already speaks MCP
MCP or REST API
Use MCP when what you're connecting is an AI client that already understands the Model Context Protocol.
Use the REST API when you're building your own integration, automation or backend code and want full control over requests and responses.
Claude Code
From the terminal, in your project folder:
claude mcp add --transport http jotly https://jotly.si/api/v1/mcp \ --header "Authorization: Bearer pk_live_YOUR_KEY"Or add it by hand in .mcp.json:
{ "mcpServers": { "jotly": { "type": "http", "url": "https://jotly.si/api/v1/mcp", "headers": { "Authorization": "Bearer pk_live_YOUR_KEY" } } }}Cursor
In your project's .cursor/mcp.json, or in ~/.cursor/mcp.json for all your projects:
{ "mcpServers": { "jotly": { "url": "https://jotly.si/api/v1/mcp", "headers": { "Authorization": "Bearer pk_live_YOUR_KEY" } } }}Codex
Codex reads its configuration from ~/.codex/config.toml. Export the key as JOTLY_API_KEY in your environment and add:
[mcp_servers.jotly]url = "https://jotly.si/api/v1/mcp"# The key lives in an environment variable, not in the filebearer_token_env_var = "JOTLY_API_KEY"Test the connection
curl -s -X POST "https://jotly.si/api/v1/mcp" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'For a scheduled agent that triages the inbox, start with listConversations with inbox_state=needs_team and updated_since set to the time of its last run. Jotly doesn't push events to your agent yet: your routine sets the frequency.
So that retries don't create duplicates, createRequest, createChangelog, importAsset, createConversationReply and createConversationNote accept an idempotency_key argument: with the same key and the same arguments within 24 hours, they return the original result. The key's per-minute request limit is the same as in the REST API; past it, the tool responds with isError and the rate_limited code along with the wait in seconds.
Handling your key
Treat your API key like any other secret:
- don't write it into config files you commit to your repository
- prefer environment variables or your secrets manager when your client supports them
- create one key per agent or environment so you can revoke it without affecting the others
- revoke keys you no longer use from
Settings → API
A key can do anything inside its workspace, including deleting. Create one per agent and keep it in your secrets manager, not in the repository.
Tools
| Tool | What it does |
|---|---|
listBoards | Lists the workspace's feedback boards, in order. |
createBoard | Creates a feedback board. 409 board_name_conflict if one with that name already exists. |
getBoard | Gets a board. |
updateBoard | Updates a board's name, color, description or visibility (public or private). |
deleteBoard | Deletes an empty board. 409 board_not_empty if it still has requests. |
listRequestTags | Lists the workspace's request tags. |
createRequestTag | Creates a request tag. 409 tag_name_conflict if it already exists. |
deleteRequestTag | Deletes a request tag and removes it from every request. |
listRequests | Lists requests with filters and sorting. sort: newest, oldest, top, trending or status_updated. |
createRequest | Creates a request on behalf of a member (author { id } or { external_id }). Accepts Idempotency-Key. |
getRequest | Gets a request by id or number. |
updateRequest | Updates a request's title or body (Jotly also accepts hidden). |
deleteRequest | Deletes a request along with its votes and comments. |
listSimilarRequests | Requests similar to another one (or to a text in query), to catch duplicates. similarity ranges from 0 to 1. |
updateRequestStatus | Changes the status and emails the author, voters and followers. Optional actor: who makes the change (a team member). |
moveRequestBoard | Moves a request to another board. |
mergeRequest | Merges a duplicate request into the canonical one (into_request_id). Votes and comments move to the canonical request. |
listRequestVotes | Lists who voted on a request, newest vote first. |
addRequestVote | Votes on a request on behalf of a member (for example, after a sales call). |
deleteRequestVote | Removes a member's vote (voter_id or voter_external_id). |
listRequestSubscriptions | Lists who follows a request (and gets its notifications). |
addRequestSubscription | Subscribes a member to a request's notifications. |
deleteRequestSubscription | Removes a member's subscription (subscriber_id or subscriber_external_id). |
listRequestTagAssignments | Lists a request's tags. |
addRequestTags | Adds tags to a request without removing the ones it already has. |
deleteRequestTagAssignment | Removes a tag from a request. |
setRequestTags | Replaces all of a request's tags (Jotly). |
listRequestComments | Lists a request's comments in one-level threads (replies), pinned first. Includes internal notes. |
createRequestComment | Comments on a request on behalf of a member (author). Without author, the team comments. visibility internal = a note the customer doesn't see. A reply to a reply stays in the same thread. |
getComment | Gets a comment. |
updateComment | Edits a comment's text. |
deleteComment | Deletes a comment and its replies. |
listCommentVotes | Lists who voted on a comment. |
addCommentVote | Votes on a comment on behalf of a member. |
deleteCommentVote | Removes a member's vote on a comment (voter_id or voter_external_id). |
pinComment | Pins a comment to the top of the thread. |
unpinComment | Unpins a comment. |
listChangelogTags | Lists changelog tags (New feature, Improvement, Fix...). |
createChangelogTag | Creates a changelog tag. 409 tag_name_conflict if it already exists. |
deleteChangelogTag | Deletes a changelog tag and removes it from every entry. |
listChangelogs | Lists changelog entries. status: draft, scheduled or published. |
createChangelog | Creates a changelog entry. It stays a draft unless status=published (or a future publish_at to schedule it). Without authors, the key's creator signs it. Accepts Idempotency-Key. |
getChangelog | Gets a changelog entry with its tags, authors and linked requests. |
updateChangelog | Edits an entry; status publishes it, schedules it (with publish_at) or moves it back to draft. |
deleteChangelog | Deletes a changelog entry. |
listChangelogTagAssignments | Lists a changelog entry's tags. |
addChangelogTags | Adds tags to an entry without removing the ones it already has. |
deleteChangelogTagAssignment | Removes a tag from an entry. |
listChangelogAuthors | Lists an entry's authors. |
addChangelogAuthors | Adds authors to an entry. They must be team members (invalid_changelog_author otherwise). |
deleteChangelogAuthor | Removes an author from an entry. |
listChangelogRequests | Lists the requests an entry closes. |
addChangelogRequests | Links requests (by id or number) to an entry; their voters see that it shipped. |
deleteChangelogRequest | Unlinks a request from an entry. |
listMembers | Lists members (people who vote, post or chat). type: user, admin or owner. company_external_id filters by company. |
lookupMember | Finds a member by external_id or email. |
syncMember | Creates or updates a member by external_id (your user id). Traits are merged. companies[] creates or updates their companies and links them. |
getMember | Gets a member. 410 member_erased if their data was erased. |
updateMember | Edits a member: external_id, email, name, avatar or traits (merged; a trait set to null is removed). |
syncCompany | Creates or updates a company by external_id. name is required when creating it; traits are merged. |
listCompanies | Lists companies. Filter by search (name or domain), external_id or domain. |
lookupCompany | Finds a company by external_id. |
getCompany | Gets a company. |
updateCompany | Edits a company. 409 company_identity_conflict if the new external_id already belongs to another one. |
listCompanyMembers | Lists a company's members. |
addCompanyMember | Links a member ({ id } or { external_id }) to a company. |
deleteCompanyMember | Unlinks a member from a company. |
listConversations | Lists conversations. inbox_state: needs_team (waiting on the team), ai_handling (handled by the AI) or waiting_on_customer. For scheduled agents: updated_since + inbox_state=needs_team. |
getConversation | Gets a conversation. Its messages are in listConversationTimeline. |
listConversationTimeline | A conversation's timeline, oldest to newest. types: message, note, ai_handoff, resolved, reopened, assigned, unassigned. |
createConversationReply | Replies to the customer as a team member (actor; without actor, the key's creator). The AI steps out of the conversation; it reopens if it was resolved. Accepts Idempotency-Key. |
createConversationNote | Adds an internal note (the customer doesn't see it). Accepts Idempotency-Key. |
updateConversationStatus | Resolves or reopens a conversation. 409 conversation_status_conflict if it's archived or marked as spam. |
updateConversationAssignee | Assigns the conversation to a team member (assignee { id } or { external_id }, or assignee_email) or unassigns it with null. |
importAsset | Imports an image from a public HTTPS URL (up to 3 redirects, 10 MiB, PNG, JPEG, GIF or WebP). Returns the asset with its permanent URL. |
getAsset | Gets a workspace asset. |
listRequestAttachments | Lists a request's attached images (signed URLs that expire in one hour). |
addRequestAttachments | Attaches images to a request from URLs (up to 4 per request). |
deleteRequestAttachment | Removes an attachment from a request. |
listCommentAttachments | Lists a comment's attached images. |
addCommentAttachments | Attaches images to a comment from URLs (up to 4 per comment). |
deleteCommentAttachment | Removes an attachment from a comment. |
See also MCP server