We have issued the refund.
" \
--comment-type reply
```
**Required arguments:**
* `` - Ticket number or UUID
| Flag | Type | Required | Default | Description |
| - | - | - | - | - |
| `--attach` | `stringArray` | | `[]` | Path to a file to attach (repeat the flag for more than one) |
| `--comment-type` | `string` | | `reply` | Comment type: reply or note |
| `--content` | `string` | Yes | | Comment content (HTML) |
`--attach` attaches a file to the comment, and the flag repeats for more than one file. As with
`tickets create`, every file is uploaded first, so a file that cannot be read or uploaded fails
the command and adds no comment.
```bash theme={"system"}
neetodesk tickets comments create 1042 \
--content "Here is the replacement part.
" \
--attach ~/Desktop/part-diagram.pdf
```
Use [`attachments upload`](/cli-reference/attachments#upload-a-file) when the file has to be
attached by a later call than the one that uploads it.
`tickets drafts create --attach` keeps a file on a draft until the draft is sent.
## Create a draft
This command saves an unsent draft on a ticket, replacing whichever draft the ticket already holds of that
type. A reply drafted for an agent, named with `--author-email`, gets that agent's saved
signature appended. Use this when a reply needs review before it is sent.
```bash theme={"system"}
neetodesk tickets drafts create 1042 \
--content "Checking with the payments team.
" \
--author-email oliver.smith@example.com
```
**Required arguments:**
* `` - Ticket number or UUID
| Flag | Type | Required | Default | Description |
| - | - | - | - | - |
| `--attach` | `stringArray` | | `[]` | Path to a file to attach (repeat the flag for more than one) |
| `--author-email` | `string` | | | Author agent email |
| `--comment-type` | `string` | | `reply` | Draft type: reply or note |
| `--content` | `string` | Yes | | Draft content (HTML) |
```json Sample output (--json) theme={"system"}
{
"data": {
"draft": {
"id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"comment_type": "reply",
"created_at": "2026-08-03T11:20:05.000Z",
"updated_at": "2026-08-03T11:20:05.000Z",
"content": "Checking with the payments team.
"
}
}
}
```
The CLI can write drafts but not read them back.
# Utility commands
Source: https://apidocs.neetodesk.com/cli-reference/utility
Authentication, diagnostics, versioning, upgrades, AI-assistant setup, and shell completion.
These commands manage the CLI itself rather than a NeetoDesk resource.
## Authentication
### login
```bash theme={"system"}
neetodesk login --subdomain your-workspace
```
Opens your browser to sign in and saves credentials for the workspace. Omit `--subdomain` to be prompted.
```text Sample output theme={"system"}
Authenticated as oliver@example.com on acme.neetodesk.com.
```
### logout
```bash theme={"system"}
neetodesk logout --subdomain your-workspace
neetodesk logout --all
```
| Flag | Type | Required | Default | Description |
| - | - | - | - | - |
| `--all` | `bool` | | `false` | Sign out of every saved subdomain |
```text Sample output theme={"system"}
Signed out of acme.neetodesk.com.
```
### whoami
```bash theme={"system"}
neetodesk whoami
```
Lists each signed-in account and marks it `(default)` when only one is signed in. Pass `--subdomain ` to show one workspace.
```text Sample output theme={"system"}
Authenticated as oliver@example.com on acme.neetodesk.com (default).
```
## Diagnostics
### doctor
```bash theme={"system"}
neetodesk doctor
```
Checks authentication, API reachability, and the CLI version. See [Troubleshooting](/cli/troubleshooting). When several workspaces are signed in, pass `--subdomain `.
```text Sample output theme={"system"}
✓ Authentication: authenticated as oliver@example.com on acme.neetodesk.com
✓ API connection: https://acme.neetodesk.com (responding in 214ms)
✓ CLI version: 1.4.0
```
### version
```bash theme={"system"}
neetodesk version
```
Prints the CLI version, commit hash, and build date.
```text Sample output theme={"system"}
neetodesk 1.4.0 (commit: a1b2c3d, built: 2026-07-01)
```
### commands
```bash theme={"system"}
neetodesk commands
```
Prints the complete command and flag catalog for the installed version as JSON. It is useful in scripts and with AI assistants.
```json Sample output theme={"system"}
[
{
"command": "neetodesk tickets",
"description": "Manage tickets",
"subcommands": [
{
"command": "neetodesk tickets list",
"description": "List tickets",
"flags": [
{ "name": "status", "type": "string", "description": "Filter by status (comma-separated, e.g. open,pending)" }
]
}
]
}
]
```
## Upgrade
### update
```bash theme={"system"}
neetodesk update
```
Updates the CLI to the latest release using the method that installed it. When the binary lives in a Homebrew Cellar it runs `brew update && brew upgrade neetozone/tap/neetodesk`; on other macOS and Linux installs it downloads the shell installer to a temporary file and runs it; on Windows it runs the PowerShell installer. The installers are the same ones the [Installation](/cli/installation) page uses, fetched over HTTPS. The command prints the detected method and the command it runs, and exits non-zero if that command fails.
```text Sample output theme={"system"}
Detected Homebrew install.
Running: brew update && brew upgrade neetozone/tap/neetodesk
```
After upgrading, re-run `neetodesk completion ` and any `neetodesk setup` command you use so their files match the new version.
## AI-assistant setup
Add NeetoDesk instructions to your AI coding assistant. See [AI assistants](/cli/ai-assistants) for details.
```bash theme={"system"}
neetodesk setup claude
neetodesk setup cursor
neetodesk setup windsurf
neetodesk setup copilot
neetodesk setup gemini
neetodesk setup codex
```
```text Sample output theme={"system"}
NeetoDesk plugin extracted to /Users/oliver/.config/neetodesk/claude-plugin
To finish installation, open Claude Code and run these slash commands:
/plugin marketplace add /Users/oliver/.config/neetodesk/claude-plugin
/plugin install neetodesk@neetodesk
(Claude Code installs plugins via interactive slash commands - there is no shell equivalent today.)
```
## Shell completion
Shell completion fills in command names, subcommands, and flags when you press `Tab`. It saves you from having to remember the full command tree or look up `--help`.
`neetodesk completion ` installs completion for your shell. For zsh, bash, and PowerShell it writes the script under `~/.config/neetodesk/completions` and adds a marked block to `~/.zshrc`, `~/.bashrc`, or `~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1` that loads it the next time the shell starts. For fish it writes `~/.config/fish/completions/neetodesk.fish`, which fish loads on its own.
```bash Zsh theme={"system"}
neetodesk completion zsh
```
```bash Bash theme={"system"}
neetodesk completion bash
```
```bash Fish theme={"system"}
neetodesk completion fish
```
```powershell PowerShell theme={"system"}
neetodesk completion powershell
```
Start a new shell (or run `source ~/.zshrc`) to enable it. After upgrading the CLI, run the command again to update the available completions. Re-running it replaces the existing script and configuration without adding duplicates.
To configure it yourself, pass `--print` to write the raw script to standard output instead of installing it:
```bash theme={"system"}
neetodesk completion zsh --print
```
# AI assistants
Source: https://apidocs.neetodesk.com/cli/ai-assistants
Set up NeetoDesk for Claude, Cursor, Copilot, and other AI coding assistants.
Use the CLI with an AI coding assistant to manage NeetoDesk in plain language. The `neetodesk setup` commands add NeetoDesk instructions for your assistant, while [`--toon`](/cli/output-formats) keeps command output compact for it to read.
## Set up your assistant
Run the subcommand for the tool you use:
| Command | What it does |
| - | - |
| `neetodesk setup claude` | Extracts the NeetoDesk plugin (manifest, marketplace, session-start hook, and skill) to `~/.config/neetodesk/claude-plugin`, replacing any earlier copy. Requires `~/.claude/` to exist. |
| `neetodesk setup cursor` | Writes `.cursor/rules/neetodesk.mdc`. Overwrites the file. |
| `neetodesk setup windsurf` | Writes `.windsurf/rules/neetodesk.md`. Overwrites the file. |
| `neetodesk setup copilot` | Adds a NeetoDesk section to `.github/copilot-instructions.md`. Keeps the rest of the file. |
| `neetodesk setup gemini` | Adds a NeetoDesk section to `GEMINI.md`. Keeps the rest of the file. |
| `neetodesk setup codex` | Adds a NeetoDesk section to `AGENTS.md`. Keeps the rest of the file. |
Every subcommand except `setup claude` writes into the current directory, so run it from the root of the project the assistant works in. The `copilot`, `gemini`, and `codex` commands place their section between `` and `` markers and replace that section on re-run instead of adding a duplicate. Re-run the command after every CLI upgrade so the instructions match the installed version.
```bash theme={"system"}
neetodesk setup claude
```
`setup claude` prints two slash commands to finish the installation inside Claude Code:
```text theme={"system"}
/plugin marketplace add ~/.config/neetodesk/claude-plugin
/plugin install neetodesk@neetodesk
```
After setup, your assistant can check the commands and flags available in your installed CLI version:
```bash theme={"system"}
neetodesk commands
```
The command prints the complete catalog as JSON.
## Tips for agent workflows
Prefer `--toon` when the assistant needs to read a list or record, and
`--quiet` when it needs to pipe an identifier into the next command. See
[Output formats](/cli/output-formats).
* Authenticate once with [`neetodesk login`](/cli/authentication); the assistant reuses the stored credentials.
* When several workspaces are signed in, the assistant must pass `--subdomain ` on each command.
* Run [`neetodesk doctor`](/cli/troubleshooting) first to confirm the assistant is authenticated and connected.
# Authentication
Source: https://apidocs.neetodesk.com/cli/authentication
Sign in through the browser and manage credentials for one or more workspaces.
The CLI uses your browser to sign you in and securely saves your session, so you never need to enter an API key.
## Sign in
```bash theme={"system"}
neetodesk login --subdomain your-workspace
```
Your browser opens to complete sign-in. The CLI also prints the sign-in URL in case the browser does not open, and waits up to five minutes for you to approve. Once you approve it, the CLI saves credentials for that subdomain. If you omit `--subdomain`, the CLI prompts for it.
Your subdomain is the first part of your workspace URL - for
`https://spinkart.neetodesk.com`, the subdomain is `spinkart`. See
[Workspace subdomain](/cli/workspace-subdomain).
## Where credentials are stored
Credentials for every signed-in workspace live in a single file:
```text theme={"system"}
~/.config/neetodesk/auth.json
```
Removing this file signs you out of every workspace, the same as `neetodesk logout --all`.
## Check who you are
```bash theme={"system"}
neetodesk whoami
```
Lists every signed-in account. When exactly one is signed in, it is marked `(default)`. Pass `--subdomain ` to show just one.
## Sign out
```bash theme={"system"}
neetodesk logout --subdomain your-workspace # remove one workspace
neetodesk logout --all # remove every workspace
neetodesk logout # remove the only one, if a single workspace is signed in
```
## Working with multiple workspaces
You can sign in to several subdomains at once. To add another workspace, name its subdomain:
```bash theme={"system"}
neetodesk login --subdomain globex
```
When more than one workspace is signed in, every command that talks to the API requires `--subdomain`. If you omit it, the CLI errors and lists the signed-in workspaces.
```bash theme={"system"}
neetodesk tickets list --subdomain acme
neetodesk tickets list --subdomain globex
```
With one signed-in workspace, it is the default and `--subdomain` stays optional.
## Environment variables
`NEETODESK_BASE_URL` overrides the server the CLI talks to. By default every command, including `login`, uses `https://.neetodesk.com`. Set the variable to point the CLI at a staging or local server. Every request, including the session token saved in `~/.config/neetodesk/auth.json`, goes to that server, so only point it at a server you trust:
```bash theme={"system"}
export NEETODESK_BASE_URL=https://acme.neetodesk.net
neetodesk login --subdomain acme
```
## What the CLI can reach
The CLI acts as the person who signed in, so it sees what that person's role allows. An agent restricted to their own tickets gets a `tickets list` narrowed to those tickets. The [MCP server](/mcp/authentication) applies the same boundary to an OAuth connection. An [API key](/api/authentication) reaches the whole workspace instead.
| | CLI session | API key |
| - | - | - |
| Scope | The person who signed in | The whole workspace |
| Set up by | `neetodesk login` | Generating a key in the admin panel |
| Used by | The CLI | Direct API calls |
## Troubleshooting
If sign-in or connection problems come up, run `neetodesk doctor` first. See [Troubleshooting](/cli/troubleshooting) for common issues and fixes.
# Installation
Source: https://apidocs.neetodesk.com/cli/installation
Install the neetodesk CLI on macOS, Linux, or Windows.
Install the `neetodesk` binary with the command for your platform, then [verify it](#verify-the-installation).
```bash macOS theme={"system"}
brew trust neetozone/tap
brew install neetozone/tap/neetodesk
```
```bash Linux theme={"system"}
curl -fsSL https://neetodesk.com/cli/install.sh | sh
```
```powershell Windows theme={"system"}
irm https://neetodesk.com/cli/install.ps1 | iex
```
The shell and Windows installers download the latest release for your architecture (`amd64` or `arm64`), extract it, and add `neetodesk` to your `PATH`.
## Other install methods
```bash theme={"system"}
curl -fsSL https://neetodesk.com/cli/install.sh | sh
```
Installs to `/usr/local/bin` and may prompt for `sudo`. To update a Homebrew installation, run `brew upgrade neetodesk`.
```cmd theme={"system"}
curl -fsSL https://neetodesk.com/cli/install.cmd -o install.cmd && install.cmd
```
Extracts to `%LOCALAPPDATA%\Programs\neetodesk` and adds it to your user `PATH`. This is equivalent to the PowerShell method.
## Verify the installation
Restart your terminal, then run:
```bash theme={"system"}
neetodesk --help
```
You should see the list of available commands. To check the installed version:
```bash theme={"system"}
neetodesk version
```
## Upgrade
```bash theme={"system"}
neetodesk update
```
The command detects how the CLI was installed and runs the matching upgrade: `brew update && brew upgrade neetozone/tap/neetodesk` for a Homebrew install, the shell installer for other macOS and Linux installs, and the PowerShell installer on Windows. It prints the command it runs and exits non-zero if that command fails. You can also run the install command for your platform again. See [`neetodesk update`](/cli-reference/utility#update).
After an upgrade, re-run `neetodesk completion ` and any `neetodesk setup` command you use so their files match the new version.
Next, [sign in](/cli/authentication) to a workspace and run `neetodesk doctor`
to confirm the CLI can reach NeetoDesk.
# NeetoDesk CLI
Source: https://apidocs.neetodesk.com/cli/introduction
Manage tickets, comments, customers, reports, and more from your terminal.
The **NeetoDesk CLI** (`neetodesk`) lets you manage NeetoDesk from your terminal. It covers the [v2 REST API](/api/introduction): list and create tickets, post comments and drafts, manage customers and team members, and pull reports.
It is designed for people comfortable with a terminal or HTTP. You do not need to be a developer: run `neetodesk setup` to connect an AI assistant such as Claude or Cursor, then describe the task in plain language.
## Why use the CLI?
Create tickets, post replies, and update statuses from shell scripts and cron jobs.
Pull tickets or agent and group reports, pipe them to `jq`, and create one-off reports without leaving your shell.
Page through thousands of tickets in one loop, update them unattended, and switch workspaces with a single flag.
Use token-efficient `--toon` output and one-command setup for Claude, Cursor, Copilot, and more.
## CLI vs MCP: which should I use?
NeetoDesk's [MCP server](/mcp/introduction) reaches the same resources the CLI does - tickets, comments and drafts, customers, forms, team members, and reports. The MCP server can also search tickets by subject and by the text of their comments, and the CLI can also list and show customers. Past that, neither one can do more than the other, so choose on how the work reaches NeetoDesk.
### Reach for the CLI when
* **No AI assistant should be in the loop.** A cron entry or a CI step runs `neetodesk` with nothing but the binary and the workspace you already signed in to - no assistant open, no model account, no tokens spent per run. Over MCP, something with model access has to be running before any call happens at all.
* **The output feeds another program.** `--quiet` prints the bare ticket id for the next command; `--json` returns records plus pagination for `jq`, a spreadsheet, or your own script. An assistant replies in prose you would have to copy out by hand.
* **You are working through thousands of records.** Over MCP every page is a separate tool call, and a list that long crowds out the assistant's context. The CLI pages on your terms instead: `--page-size 100` with `--json` returns `total_pages` and `total_records` alongside the records, so a loop knows how many pages are left and walks all of them unattended. Each page lands in a file or goes straight into `jq`, so nothing has to be held in memory and the size of the list stops mattering.
* **You need the same run every time.** A `neetodesk` command is plain text you can save. Drop it into a runbook or a pull request and anyone can read exactly what it will do before it runs, then run it later and get the identical call. An assistant works from a prompt rather than a fixed command, so the same request can come out as a different set of calls on a different day.
### Reach for MCP instead when
* **The details live in your chat, not in your head.** A forwarded email thread or a pasted complaint turns into the ticket with no retyping. The CLI cannot see any of it.
* **You have not decided the steps yet.** "The refund queue is a mess - sort it out" means looking at what is there and choosing. A command can only carry out a decision you have already made.
* **One request should cover several steps.** Find the open tickets about failed card payments, summarize what each customer asks for, and draft a reply on the oldest one, with no glue between commands.
* **The person doing it does not use a terminal.** NeetoDesk hosts the server, so there is nothing to install or keep updated.
You can have both. Run [`neetodesk setup claude`](/cli/ai-assistants) and your
AI assistant drives the CLI itself, so a plain-language request still ends in
an exact command you can read, repeat, and paste into a script.
## What you need
1. Access to one or more NeetoDesk workspaces.
2. Permission to manage the resources you want to work with (tickets, customers, team members, and so on).
3. The `neetodesk` binary. See [Installation](/cli/installation).
Unlike the API, which authenticates with an `X-Api-Key` header, the CLI signs
you in through your browser and stores credentials locally. See
[Authentication](/cli/authentication).
# Output formats
Source: https://apidocs.neetodesk.com/cli/output-formats
Pretty tables, JSON, quiet, and TOON - and when to use each.
Every command can return one of four output formats. In an interactive terminal, pretty output is the default. Use the flags below for machine-readable output.
| Format | Flag | Best for |
| - | - | - |
| Pretty | *(default in a terminal)* | Reading a result yourself. |
| JSON | `--json` | Machine-readable payloads with pagination metadata. |
| Quiet | `--quiet` | Piping an identifier into another command or `jq`. |
| TOON | `--toon` | Feeding list/show output back to an LLM (fewer tokens than JSON). |
If more than one is set, precedence is `--toon` > `--quiet` > `--json` > pretty. `--verbose` changes pretty output only, so it has no effect alongside the other three.
## Global flags
These flags work on every command:
| Flag | Description |
| - | - |
| `--json` | Output as JSON |
| `--quiet` | Output raw data only (no envelope) |
| `--toon` | Output in TOON format (token-optimized for AI agents) |
| `--verbose` | Expand every field of a record instead of a table |
| `--subdomain` | Override saved subdomain |
`--subdomain` selects the signed-in workspace a command targets. See [Authentication](/cli/authentication).
## Pretty
The default in a terminal: tables for lists and key-value pairs for a single record. It is intended for people, not scripts.
A table shows at most seven columns. The CLI picks identifier and name fields first (`id`, `number`, `subject`, `status`, and similar), fills the remaining columns alphabetically, and keeps any URL fields as the last columns without truncating them.
```bash theme={"system"}
neetodesk tickets list --status open
```
## Verbose
`--verbose` prints every field of every record instead of a table, one label per line, with nested objects and lists indented underneath. Use it when a table leaves out a field you need.
```bash theme={"system"}
neetodesk tickets list --status open --verbose
```
## JSON
JSON wraps the resource body with breadcrumbs and, for list commands, pagination details:
```json theme={"system"}
{
"data": [
{ "id": "0f1e4c9a-6d3b-4f92-8a71-25b0c8d4e6f3", "number": 1042, "subject": "Refund not received", "status": "open" }
],
"breadcrumbs": [
{ "label": "Show", "command": "neetodesk tickets show " },
{ "label": "Update", "command": "neetodesk tickets update " }
],
"pagination": {
"total_records": 250,
"total_pages": 9,
"current_page_number": 1,
"page_size": 30
}
}
```
For list commands, `data` is the array of records; the CLI removes the API response's resource key. For `show`, `create`, and `update`, `data` holds the API response body, such as `{ "ticket": { ... } }`.
`breadcrumbs` is omitted when empty, and `pagination` appears only on list commands. The CLI automatically uses JSON when output is piped (a non-TTY); use `--json` to force it.
## Quiet
`--quiet` returns only the `data` payload. For `create` and `update`, it prints the resource identifier; for `delete`, it prints `success`. This is useful in scripts:
```bash theme={"system"}
neetodesk tickets create \
--email oliver@example.com --subject "Refund not received" \
--description "Card was charged twice." \
--quiet
# → prints the new ticket id
```
## TOON
`--toon` encodes the same data as [TOON](https://github.com/alpkeskin/gotoon) (Token-Optimized Output Notation). It keeps the JSON shape while compressing whitespace and keys, typically using 30-60% fewer tokens. Use it when giving list or show output to an AI assistant.
```bash theme={"system"}
neetodesk tickets list --status open --toon
```
## Pagination
List commands return results in pages. The flags below control paging, and the JSON envelope's `pagination` block shows your position in the result set.
### Pagination parameters
The page of results to retrieve, starting from 1.
The number of records to return per page (max 100).
### Example usage
```bash theme={"system"}
neetodesk tickets list --page 2 --page-size 50
```
This returns the second page of tickets, with 50 records per page.
### Response structure
For list commands the JSON envelope carries a `pagination` block alongside the data:
```json theme={"system"}
{
"pagination": {
"total_records": 250,
"total_pages": 5,
"current_page_number": 2,
"page_size": 50
}
}
```
The total number of records across all pages.
The total number of pages available.
The page you are currently on.
The number of records returned per page.
### Default behavior
If you omit both flags, the CLI lets the server use its defaults: page 1 with 30 records. Either flag overrides the defaults; `--page-size` accepts up to 100.
### Best practices
* To retrieve every page, increment `--page` until `current_page_number == total_pages`.
* Use `--json` or `--toon` in scripts when you need the `pagination` block. `--quiet` removes the envelope, so it does not include pagination metadata.
* Check `total_records` before you begin to estimate the work involved.
# Troubleshooting
Source: https://apidocs.neetodesk.com/cli/troubleshooting
Diagnose authentication and connectivity issues with neetodesk doctor.
## Run the doctor
When something goes wrong, start with `neetodesk doctor`. It checks your authentication, API connection, and CLI version.
```bash theme={"system"}
neetodesk doctor
```
When multiple workspaces are signed in, name the one to check:
```bash theme={"system"}
neetodesk doctor --subdomain your-workspace
```
## Common errors
When a command fails, it prints an error message to standard error and exits with status 1. Usage errors such as an unknown flag or a missing argument add `Run 'neetodesk --help' for usage.`
**Problem**: no workspace is signed in.
**Solution**: run [`neetodesk login --subdomain `](/cli/authentication).
**Problem**: more than one workspace is signed in, so the target is ambiguous.
**Solution**: add `--subdomain ` to the command. For `logout`, `--all` signs out of every workspace.
**Problem**: the `--subdomain` value does not match any signed-in workspace.
**Solution**: use one of the listed subdomains, or [sign in](/cli/authentication) to the new one.
**Problem**: `neetodesk login` could not find a workspace at `.neetodesk.com`.
**Solution**: use the first part of your workspace URL. For `https://spinkart.neetodesk.com`, enter `spinkart`.
**Problem**: the browser sign-in was not approved before the session expired. `NeetoDesk CLI authentication timed out after 5 minutes. Please try again.` means the CLI stopped waiting.
**Solution**: run `neetodesk login` again and approve the sign-in in the browser. If the browser does not open, visit the URL the CLI prints.
**Problem**: the CLI could not reach the API.
**Solution**: check your network and run `neetodesk doctor`. If [`NEETODESK_BASE_URL`](/cli/authentication#environment-variables) is set, confirm it points at a reachable server.
**Problem**: a required flag was omitted.
**Solution**: check the command's [reference page](/cli-reference/overview) or run `neetodesk --help` for the required flags.
**Problem**: the server rejected the request. The CLI prints `API error (): `, one line per additional error, and a `Suggestion:` line for common statuses: 401 (sign in again), 403 (no permission), 404 (check the ID), 422 (check required fields with `--help`), and 429 (rate limited, wait and retry).
**Solution**: follow the suggestion. For 401, run `neetodesk login` to refresh the session.
**Problem**: the CLI acts as the person who signed in, so an agent restricted to their own tickets or to their groups sees a narrowed list.
**Solution**: check which account is signed in with `neetodesk whoami`. If the account is right, the limit comes from the role. See [What the CLI can reach](/cli/authentication#what-the-cli-can-reach).
**Problem**: `neetodesk setup claude` requires Claude Code to have been run at least once.
**Solution**: install and open Claude Code, then run the command again.
## Check the version
```bash theme={"system"}
neetodesk version
```
Prints the CLI version, commit hash, and build date - useful when reporting an issue. To upgrade, run [`neetodesk update`](/cli-reference/utility#update).
# Workspace subdomain
Source: https://apidocs.neetodesk.com/cli/workspace-subdomain
Find your workspace subdomain and tell the CLI which workspace to use.
Every NeetoDesk workspace has its own subdomain. It is the part of your workspace URL before `neetodesk.com`. For `https://spinkart.neetodesk.com`, the subdomain is `spinkart`.
The CLI uses the subdomain to know which workspace to sign in to and which workspace a command works on.
## Find your subdomain
Sign in to NeetoDesk in your browser and look at the address bar.
For `https://green-earth.neetodesk.com`, the subdomain is `green-earth`.
## Sign in with it
Pass the subdomain to `login`:
```bash theme={"system"}
neetodesk login --subdomain green-earth
```
If you omit `--subdomain`, the CLI asks for it.
## Pick a workspace for a command
With one signed-in workspace, the CLI uses it and `--subdomain` stays optional. When you sign in to more than one, add `--subdomain` to every command that talks to the API:
```bash theme={"system"}
neetodesk tickets list --subdomain green-earth
```
See [Authentication](/cli/authentication) for signing in to several workspaces.
# Authentication
Source: https://apidocs.neetodesk.com/mcp/authentication
Connect as yourself with OAuth, or as the whole workspace with an API key.
The MCP server accepts two kinds of credentials. The one you pick decides **what the assistant can see**.
| | OAuth | API key |
| - | - | - |
| Scope | The person who approved it | The whole workspace |
| Identity | Tied to a NeetoDesk user | Tied to nobody |
| Set up by | Signing in to NeetoDesk from the assistant | Pasting a key into a config file |
| Used by | Every client except Antigravity | Every client except Claude and ChatGPT |
| Reaches several workspaces | Yes, tick them while approving | No, one key is one workspace |
| Revoked by | Removing the connector | Revoking the key in workspace settings |
In Claude Code, Codex, Cursor, Gemini CLI, VS Code, and Windsurf, leave the key out of the config to sign in with OAuth. Put the key in to use the API key. Both expose the same [tools](/mcp/tools).
## OAuth, scoped to you
The assistant acts as the person who approved the connection. Lists and searches show only what that person can see. A ticket they cannot open is refused. An agent restricted to their own tickets or their groups gets ticket lists narrowed to those tickets.
Use OAuth when a person drives the assistant. For the steps, see [Connect agent](/mcp/connect#sign-in-with-your-neetodesk-account).
| Detail | Value |
| - | - |
| Metadata | `https://connect.neetodesk.com/.well-known/oauth-authorization-server` |
| Grant types | `authorization_code` and `refresh_token`, with PKCE (`S256`) required |
| Scopes | `read`, `write`, `delete`, `offline_access` |
| Client registration | Dynamic, per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591), at `https://connect.neetodesk.com/mcp/oauth/register`. Loopback redirect URIs are accepted. |
| Refresh | Refresh tokens are issued, so you stay signed in |
| Revoke | Remove the connector from your assistant |
### What you approve
| Scope | Shown as | What it means | Always granted |
| - | - | - | - |
| `read` | **Read** | See everything in the selected workspaces. | Yes |
| `offline_access` | **Stay connected** | Keep working without asking you to sign in again. | Yes |
| `write` | **Create and update** | Add new items and change existing ones. | No |
| `delete` | **Delete** | Permanently delete items. This cannot be undone. | No |
If you leave a scope unticked, a tool that needs it is refused with a message that names the scope. For example, a read-only connection can list and search tickets but cannot post a comment, update a ticket, or delete anything.
One OAuth connection can reach several workspaces. Name the workspace in your prompt, or ask the assistant to list the workspaces it can reach. Every tool takes an optional `workspace` argument for this.
## API key, scoped to the workspace
An API key is not tied to a person. Every tool call reaches the whole workspace, and role checks do not run. Use a key for automation that must see every ticket. Do not use one on a machine where the person should see only their own tickets.
The key is the same one the [REST API](/api/authentication) uses. Put it in the server entry of the assistant's config file:
```json theme={"system"}
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
```
For the full config of each client, see [Connect agent](/mcp/connect#use-an-api-key). [Learn how to generate your API key.](https://help.neetodesk.com/articles/api-keys)
A key belongs to one workspace. To reach two workspaces, add the server twice, with a different key and a different server name in each entry.
An API key gives access to every ticket and customer in the workspace. Treat it like a password: keep it out of shared config files and commits, and revoke it if it leaks.
## How the three interfaces authenticate
| Interface | Credential | Scope | Where it goes |
| - | - | - | - |
| [REST API](/api/authentication) | API key | Workspace | `X-Api-Key` header on each request. |
| [CLI](/cli/authentication) | Browser sign-in, stored per workspace | The signed-in user | `~/.config/neetodesk/auth.json`. No API key involved. |
| MCP | OAuth, or an API key | The approving user, or the workspace | Browser approval, or an `Authorization: Bearer` header in the assistant's config file. |
# Connect agent
Source: https://apidocs.neetodesk.com/mcp/connect
Step-by-step setup for Claude, ChatGPT, and other AI agents.
## Sign in with your NeetoDesk account
1. Visit the [NeetoDesk listing](https://claude.ai/directory/neetodesk) in Anthropic's connector directory.
2. Click **Connect to Claude**.
3. Sign in to NeetoDesk. See [What the sign-in looks like](#what-the-sign-in-looks-like).
NeetoDesk then works in claude.ai, the Claude desktop app, Claude mobile, and Cowork.
If you see **Request** instead of **Connect to Claude**, your Team plan admin must turn NeetoDesk on under **[Organization settings > Connectors](https://claude.ai/admin-settings/connectors)**. After that, click **Connect to Claude**.
To disconnect, open **[Customize > Connectors](https://claude.ai/customize/connectors)** and remove NeetoDesk.
Use this only if your organization blocks the connector directory.
1. Open **[Customize > Connectors](https://claude.ai/customize/connectors)**, click **Add**, then click **Add custom connector**.
2. Enter the name NeetoDesk and paste the server URL.
```
https://connect.neetodesk.com/mcp/messages
```
3. Click **Continue** and sign in to NeetoDesk.
1. Go to [chatgpt.com](https://chatgpt.com) and open **Settings → Security and login**.
2. Scroll to the bottom of the page and turn on **Developer mode**. It is below **Lockdown mode**.
ChatGPT does not allow **Lockdown mode** and **Developer mode** at the same time. If **Lockdown mode** is on, turn it off first. If you still do not see **Developer mode**, your ChatGPT workspace admin has turned it off. Ask them to allow it.
3. Go to [chatgpt.com/plugins](https://chatgpt.com/plugins) and click the **plus** button.
4. In **New Plugin**, enter the name NeetoDesk, set **Connection** to **Server URL**, and paste:
```
https://connect.neetodesk.com/mcp/messages
```
5. Leave **Authentication** on **OAuth**.
6. Tick **I understand and want to continue**, then click **Create**.
7. Sign in to NeetoDesk. See [What the sign-in looks like](#what-the-sign-in-looks-like).
The desktop app does not need **Developer mode**.
1. Click your profile picture at the bottom left, then click **Settings**.
2. Click **Plugins**. Click **Add**, then click **Add MCP server**.
3. Enter the name NeetoDesk and set **Type** to **Streamable HTTP**.
4. In **URL**, paste:
```
https://connect.neetodesk.com/mcp/messages
```
5. Leave the other fields empty and click **Save**.
6. Click the **MCPs** tab. Next to **neetodesk**, click **Authenticate**.
7. Sign in to NeetoDesk. See [What the sign-in looks like](#what-the-sign-in-looks-like).
1. Add the server:
```bash theme={"system"}
claude mcp add --transport http neetodesk https://connect.neetodesk.com/mcp/messages
```
2. Start Claude Code and run `/mcp`.
3. Pick **neetodesk** and sign in to NeetoDesk in the browser.
1. Add the server to `~/.cursor/mcp.json`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"url": "https://connect.neetodesk.com/mcp/messages"
}
}
}
```
2. Restart Cursor.
3. Sign in to NeetoDesk when Cursor asks.
1. Add the server to `~/.gemini/settings.json`. Use `httpUrl`, not `url`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"httpUrl": "https://connect.neetodesk.com/mcp/messages"
}
}
}
```
2. Start Gemini CLI and sign in to NeetoDesk when it asks.
1. Add the server to `~/.codex/config.toml`:
```toml theme={"system"}
[mcp_servers.neetodesk]
url = "https://connect.neetodesk.com/mcp/messages"
```
2. Run this command and sign in to NeetoDesk in the browser:
```bash theme={"system"}
codex mcp login neetodesk
```
You need VS Code 1.99 or later, with GitHub Copilot in **Agent** mode.
1. Create `.vscode/mcp.json` in your workspace. Use `servers`, not `mcpServers`:
```json theme={"system"}
{
"servers": {
"neetodesk": {
"type": "http",
"url": "https://connect.neetodesk.com/mcp/messages"
}
}
}
```
2. Sign in to NeetoDesk when VS Code asks.
1. Add the server to `~/.codeium/windsurf/mcp_config.json`. Use `serverUrl`, not `url`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"serverUrl": "https://connect.neetodesk.com/mcp/messages"
}
}
}
```
2. Turn on the server under **Settings → Cascade → MCP Servers**.
3. Sign in to NeetoDesk when Windsurf asks.
### What the sign-in looks like
Enter your workspace subdomain, or paste your workspace URL. For `acme.neetodesk.com`, enter `acme`. The **Find your workspace subdomain** link on this screen opens the [Workspace subdomain](/mcp/workspace-subdomain) page.
Sign in to that workspace, if you are not signed in already.
**Read** and **Stay connected** are always on. Tick **Create and update** and **Delete** only if the assistant needs them.
One connection can reach more than one workspace at the same time. The workspace you entered is always connected. If your email belongs to other workspaces, tick any you also want under **Additional workspaces to connect**. The permissions you approve apply to all of them.
Click **Authorize**.
To add a workspace or a permission later, sign in again and tick it.
## Use an API key
An API key reaches the whole workspace, not only what you can see. Use it for automation. See [Authentication](/mcp/authentication).
1. Add the server to `~/.claude.json`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"type": "http",
"url": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
2. Restart Claude Code.
1. Add the server to `~/.cursor/mcp.json`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"type": "http",
"url": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
2. Restart Cursor.
1. Add the server to `~/.gemini/settings.json`. Use `httpUrl`, not `url`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"httpUrl": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
2. Restart Gemini CLI.
1. Add the server to `~/.codex/config.toml`:
```toml theme={"system"}
[mcp_servers.neetodesk]
url = "https://connect.neetodesk.com/mcp/messages"
bearer_token_env_var = "NEETODESK_API_KEY"
```
2. Set `NEETODESK_API_KEY` to your API key in the shell where you run Codex.
You need VS Code 1.99 or later, with GitHub Copilot in **Agent** mode.
1. Create `.vscode/mcp.json` in your workspace:
```json theme={"system"}
{
"inputs": [
{
"type": "promptString",
"id": "neetodesk-api-key",
"description": "NeetoDesk API key",
"password": true
}
],
"servers": {
"neetodesk": {
"type": "http",
"url": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer ${input:neetodesk-api-key}"
}
}
}
}
```
2. Paste your API key when VS Code asks.
This file lives in your project. Do not paste the key into the file itself, or it can end up in a commit.
1. Add the server to `~/.codeium/windsurf/mcp_config.json`. Use `serverUrl`, not `url`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"serverUrl": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
2. Turn on the server under **Settings → Cascade → MCP Servers**.
1. Open **Settings → Customizations → Open MCP Config**.
2. Add the server to `mcp_config.json`. Use `serverUrl`, not `url`:
```json theme={"system"}
{
"mcpServers": {
"neetodesk": {
"serverUrl": "https://connect.neetodesk.com/mcp/messages",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
```
3. Reload Antigravity.
Replace `YOUR_API_KEY` with a key from your workspace. [Learn how to generate your API key.](https://help.neetodesk.com/articles/api-keys)
## Confirm the connection
Ask the assistant *"List my open NeetoDesk tickets."* If it answers with your tickets, the connection works. For more prompts, see [Examples](/mcp/examples). If it does not work, see [Troubleshooting](/mcp/troubleshooting).
# Examples
Source: https://apidocs.neetodesk.com/mcp/examples
Prompts to try once your assistant is connected to NeetoDesk.
Once [connected](/mcp/connect), ask your assistant in plain language. It picks the right [tools](/mcp/tools), so you do not need to know any tool names. Try prompts like these:
## Triage a queue
> List open tickets in my workspace.
> Show me all open tickets assigned to me, newest first.
> Which of our open tickets are marked high priority?
An OAuth connection answers these within your own permissions, so "assigned to me" and "our
tickets" mean what they would mean in the web app. See
[Authentication](/mcp/authentication).
## Read a ticket
> Show me ticket #42 with its full description.
> List all comments on ticket #42.
> Summarize the conversation on ticket #42 and tell me what the customer is still waiting for.
Tickets can be named by number or by UUID, so `#42` is enough.
## Search
> Find tickets mentioning "refund not received".
> Search our tickets for anything about failed card payments in the last week.
Search covers ticket subjects and the text of their comments, and returns the most recent
matches with a snippet of the text that matched.
## Reply and draft
> Draft a reply on ticket #42 thanking the customer for their patience and asking for their
> order number.
> Add an internal note on ticket #42 saying the payments team is investigating.
A draft is saved on the ticket for you to review and send from NeetoDesk. A reply is published
on the ticket where the customer reads it, and a note stays internal. Neither is emailed to the
customer by the assistant.
## Create and update
> Create a ticket for [customer@example.com](mailto:customer@example.com) with subject "Login issue" and description
> "Customer cannot reset their password."
> Update ticket #42 to resolved and set its priority to low.
> Assign ticket #42 to [oliver.smith@example.com](mailto:oliver.smith@example.com).
## Attach a file
> Attach the image at [https://example.com/printer-error.png](https://example.com/printer-error.png) to ticket #42.
That address is fetched and stored in one step, so it works in any assistant. The address has to
be reachable without a login.
> File a ticket for [customer@example.com](mailto:customer@example.com) about the jammed printer and attach
> \~/Desktop/printer.png.
A file on your own machine takes an extra step: the assistant reserves the upload, sends the
bytes itself, then attaches the result. That needs an assistant that can make an HTTP request of
its own, which a terminal-based one can and a chat-only one cannot. If yours cannot, upload the
file with [`neetodesk attachments upload`](/cli-reference/attachments) and give the assistant the
`signed_id` it prints.
## People
> List the team members in my workspace.
> Invite [sam.smith@example.com](mailto:sam.smith@example.com) to the workspace as an Agent.
> Create a customer for [sam.smith@example.com](mailto:sam.smith@example.com) named Sam Smith.
## Reports
> How many tickets changed status in the last 7 days?
> Show me agent response-time reports for this month.
> Which group had the fastest average resolution time over the last 30 days?
> Summarize our customer satisfaction survey results for July.
## Forms
> List the enabled web forms in my workspace.
## Chain requests together
The tools compose, so a single request can span several of them:
> Find every open ticket about refunds, summarize what each customer is asking for, and draft a
> holding reply on the oldest one.
> Compare this month's agent performance against last month and tell me who improved most.
Ask the assistant to confirm before it posts a reply, changes a ticket's status, or deactivates a team member. MCP actions affect your live workspace.
# NeetoDesk MCP
Source: https://apidocs.neetodesk.com/mcp/introduction
Manage your NeetoDesk tickets from an AI assistant in plain language.
The NeetoDesk **MCP server** lets your AI assistant manage your helpdesk from plain-language requests. Ask it to list tickets, reply to a customer, save a draft, or pull a performance report, and it works with your NeetoDesk workspace for you.
[MCP](https://modelcontextprotocol.io) (Model Context Protocol) is an open standard that connects AI assistants to tools such as NeetoDesk. You describe the task; the assistant uses the right tool.
## What you can do
List, search, read, create, and update tickets.
Post a reply or internal note, and save a draft on a ticket.
Ticket volume, agent and group performance, and satisfaction surveys.
Ask your assistant to triage a queue, draft a reply, or pull a performance report for you.
## MCP vs CLI: which should I use?
NeetoDesk's [CLI](/cli/introduction) reaches the same resources MCP does - tickets, comments and drafts, customers, forms, team members, and reports. The one thing it has no equivalent for is `search`, which looks through ticket subjects and the text of their comments. Otherwise, neither one can do more than the other, so choose on how the work reaches NeetoDesk.
### Reach for MCP when
* **The details live in your chat, not in your head.** A forwarded email thread, or a complaint pasted out of Slack, turns into a ticket with the customer, subject, and description already filled in. The CLI cannot see any of it.
* **You have not decided the steps yet.** "The refund queue is a mess - sort it out" means reading a thirty-comment thread, judging whether the customer is still waiting, and only then picking a status. A command can only carry out a decision you have already made.
* **One request should cover several steps.** Find the open tickets about failed card payments, summarize what each customer is asking for, and draft a holding reply on the oldest one, with no glue between commands.
* **The person doing it does not use a terminal.** NeetoDesk hosts the server, so there is nothing to install or keep updated.
### Reach for the CLI instead when
* **No AI assistant should be in the loop.** A nightly cron entry that sweeps the pending queue runs the CLI with nothing but the binary and a saved session - no assistant open, no model account, no tokens spent per run. Every MCP call needs something with model access running.
* **The output feeds another program.** The CLI prints the bare ticket id with `--quiet`, or a `data` envelope with `--json` that goes straight into `jq`, a spreadsheet, or your own dashboard. Here you get prose you would have to copy out by hand.
* **You are working through thousands of records.** Here every page is a separate tool call, and a queue that long crowds out the assistant's context. The CLI takes `--page-size 100` and returns `current_page_number`, `total_pages`, `total_records`, and `page_size` alongside the tickets, so a shell loop walks the whole queue unattended and writes each page to a file or into `jq` - the size of the queue stops mattering.
* **The run has to be repeatable and reviewable.** A command is the artifact: it records exactly what ran and repeats identically. Ask twice here and the assistant may take a different route.
You can have both. Run [`neetodesk setup claude`](/cli/ai-assistants) and the
same assistant drives the CLI for you, so a plain-language request still ends
in an exact command you can read, repeat, and paste into a script.
Ask your assistant to show you what it plans to do before it posts a reply, changes a
ticket's status, or deactivates a team member. These tools act on your live workspace.
## What you need
* An AI assistant that supports MCP, such as Claude, ChatGPT, Claude Code, Codex, Cursor, Gemini CLI, VS Code with GitHub Copilot, Windsurf, or Antigravity.
* A NeetoDesk account. You need an API key only if the assistant must reach the whole workspace, or if you use Antigravity. See [Authentication](/mcp/authentication).
[Connect your assistant](/mcp/connect) to get started.
# MCP server
Source: https://apidocs.neetodesk.com/mcp/server
The NeetoDesk MCP server endpoint, transport, and the sign-in options each agent supports.
NeetoDesk hosts the MCP server. There is nothing to install.
| | |
| - | - |
| **Endpoint** | `https://connect.neetodesk.com/mcp/messages` |
| **Transport** | HTTP |
| **Authentication** | OAuth, or `Authorization: Bearer YOUR_API_KEY` |
## How each agent connects
| Agent | Sign in with NeetoDesk | API key |
| - | - | - |
| Claude, ChatGPT | Yes | No |
| Claude Code, Codex, Cursor, Gemini CLI, VS Code, Windsurf | Yes | Yes |
| Antigravity | Not documented | Yes |
When you sign in, the assistant sees only what you can see. An API key reaches the whole workspace. See [Authentication](/mcp/authentication).
For the setup steps, see [Connect agent](/mcp/connect).
# Tools
Source: https://apidocs.neetodesk.com/mcp/tools
Every tool the NeetoDesk MCP server exposes, grouped by resource.
You do not need these names to use the server. Ask in plain language and the assistant picks the right tool. The list is here for when you want to know exactly what a connection can do. Most tools match a [REST API](/api/introduction) endpoint and link to its reference.
Every tool takes an optional `workspace` argument, the subdomain of a connected workspace. Left out, it runs against the workspace the connection was set up with. `ListWorkspaces` reports the values you can pass.
What a tool returns depends on the credential. Over [OAuth](/mcp/authentication#oauth-scoped-to-you), lists come back filtered to the permissions of the person who approved the connection, and a record they cannot open is refused rather than returned; an [API key](/mcp/authentication#api-key-scoped-to-the-workspace) reaches the whole workspace. A tool that creates, updates, or deletes is also refused when the connection was not granted the **Create and update** or **Delete** permission.
## Workspaces
| Tool | What it does |
| - | - |
| `ListWorkspaces` | Lists the workspaces this connection can reach. Other tools take a `workspace` argument to target one of them. |
`ListWorkspaces` has no REST endpoint, which is why it is not linked.
An OAuth grant covers every workspace ticked on the **Authorize access** screen, which can be
more than one. An API key covers the workspace it was generated in.
## Tickets
| Tool | What it does |
| - | - |
| [`ListTickets`](/api/tickets/list) | Lists the tickets the caller can access, newest first, with each ticket's subject, status, priority, channel, assigned agent and customer. Can be narrowed to one or more statuses. |
| [`GetTicket`](/api/tickets/get) | Returns one ticket in full, including its description and the agent, customer, group and tags it is associated with. |
| [`CreateTicket`](/api/tickets/create) | Creates a ticket for a customer, identified by their email address. The description becomes the ticket's first comment, and any `attachments` are attached to it. |
| [`UpdateTicket`](/api/tickets/update) | Updates a ticket's subject, status, priority, category, assigned agent, group or tags, and adds any `attachments` to the ticket's opening message. A ticket that has no opening message is refused, and the file goes on a comment instead. |
| `search` | Searches tickets by subject and by the text of their comments, returning up to 25 of the most recent matches, each with a snippet of the matching text. |
| `fetch` | Returns one ticket in full, with every comment on it and the ticket's URL. |
Tickets are addressed by ticket number or by UUID, so `42` and the UUID both work.
`search` and `fetch` are the two tools the
[ChatGPT connector specification](https://developers.openai.com/api/docs/mcp) requires, so they
overlap with `ListTickets` and `GetTicket` rather than adding anything new. They have no
separate REST endpoint, which is why they are not linked above.
## Comments and drafts
| Tool | What it does |
| - | - |
| [`ListTicketComments`](/api/comments/list) | Lists a ticket's comments in the order they were added, with each comment's type, author's email address and body. |
| [`GetTicketComment`](/api/comments/get) | Returns one comment on a ticket, with its type, author's email address and body. |
| [`CreateTicketComment`](/api/comments/create) | Adds a comment to a ticket, with any `attachments` attached to it. A reply is published where the customer reads it; a note stays internal. Neither is emailed to the customer. |
| [`CreateTicketDraft`](/api/comments/create-draft) | Saves an unsent draft on a ticket, replacing the draft it already holds of that type, and keeps any `attachments` on it until the draft is sent. Passing `attachments` replaces the files the draft held; omitting it leaves them alone, and an empty array clears them. A reply drafted for an agent gets that agent's saved signature appended. |
A ticket holds at most one draft per comment type. There is no tool for reading a draft back, or
for editing a comment once it is posted.
## Attachments
A tool call carries JSON, never a file, so a file reaches the workspace one of two ways. Either
route ends with a `signed_id` to pass in `attachments` on `CreateTicket`, `CreateTicketComment`,
`UpdateTicket` or `CreateTicketDraft`.
| Tool | What it does |
| - | - |
| [`UploadAttachment`](/api/attachments/create) | Stores a file that is already published on the web, by fetching it from its https address. One call, and the assistant needs no ability of its own to move the file. |
| [`CreateAttachmentUpload`](/api/attachments/create-upload) | Reserves storage for a file on the caller's machine and returns an `upload_url` with the `upload_headers` to send. The assistant then sends the bytes itself with an HTTP `PUT`, so this route needs a client that can make that request - a terminal-based assistant can, a chat-only one cannot. |
`checksum` for a reservation is the base64-encoded MD5 digest of the file, which is the form
`Content-MD5` takes, not the hex digest.
`UploadAttachment` is the one tool where NeetoDesk makes an outbound request to an address the
assistant chose. The address must be `https`, and one that resolves onto a private or
link-local network is refused, so the tool cannot be steered at infrastructure that is only
reachable from inside NeetoDesk. The file also has to be reachable without credentials, which
means the tool cannot read anything behind your own login.
A `signed_id` is only accepted in the workspace that uploaded it. A reservation whose bytes never
arrived is refused at attach time rather than left as a broken attachment, and a file that is
never attached to anything is cleaned up automatically.
Attaching applies the workspace attachment limit, 10 MB by default, which is smaller than the
50 MB upload limit. A file between the two is stored but refused at attach time.
`UpdateTicket` adds files to the ticket's opening message, beside the ones already there, and a
ticket with no opening message cannot take them that way. A draft only holds the ids until it is
sent, so nothing is attached to the ticket at that point.
## Customers
| Tool | What it does |
| - | - |
| [`CreateCustomer`](/api/customers/create) | Creates a customer. At least one email address is required, and the first one given becomes the primary. |
There is no tool for reading or updating a customer. A ticket reports its customer's email
address, so read the ticket instead.
## Team members
| Tool | What it does |
| - | - |
| [`ListTeamMembers`](/api/team-members/list) | Lists active team members with their ID, email address, name, time zone and role. Can be narrowed to one exact email address. |
| [`GetTeamMember`](/api/team-members/get) | Returns one active team member's email address, name, time zone and role. |
| [`CreateTeamMember`](/api/team-members/add) | Invites one or more people to the workspace. Each address is emailed an invitation unless invitation emails are turned off. |
| [`UpdateTeamMember`](/api/team-members/update) | Updates a team member's email address, name, time zone or role. |
| [`DeleteTeamMember`](/api/team-members/remove) | Deactivates a team member, ending their access. Their user record is kept so the tickets and comments they handled stay attributed to them. |
Managing team members needs the member-management permission on the connected account.
## Forms
| Tool | What it does |
| - | - |
| [`ListForms`](/api/forms/list) | Lists the workspace's enabled web forms, the forms through which customers raise tickets. |
## Reports
| Tool | What it does |
| - | - |
| [`GetTicketReports`](/api/reports/get-ticket-volume) | Reports how many tickets entered each status over a date range. |
| [`GetTicketTimeSeriesReport`](/api/reports/get-ticket-time-series) | Reports how many tickets were opened and how many were closed on each day of a date range. |
| [`GetAgentReports`](/api/reports/get-agent-performance) | Reports each agent's average first-response time, average resolution time, and assigned and closed ticket counts over a date range, fastest first. |
| [`GetGroupReports`](/api/reports/get-team-performance) | The same metrics per group instead of per agent. |
| [`GetSurveyReports`](/api/reports/get-customer-satisfaction) | Reports how customers answered each satisfaction survey over a date range, as a count per answer choice. |
# Troubleshooting
Source: https://apidocs.neetodesk.com/mcp/troubleshooting
What to check when an assistant cannot reach NeetoDesk.
**Restart the client first.** Every client reads its MCP config at startup, and Cursor can also reload the window from the command palette. Then check the items for your kind of client.
On Claude or ChatGPT, confirm that you added the connector and finished the sign-in. In ChatGPT, the plugin is created only after you tick **I understand and want to continue**.
On Claude Code, Cursor, Gemini CLI, Codex, VS Code, Windsurf, or Antigravity, which read a config file:
1. **Check the config key.** Claude Code, Cursor, Gemini CLI, Windsurf, and Antigravity nest servers under `mcpServers`. VS Code uses `servers`, and Codex uses a `[mcp_servers.]` section in TOML. A server under the wrong key is ignored silently.
2. **Check the URL key.** Windsurf and Antigravity expect `serverUrl`, and Gemini CLI expects `httpUrl` because it reserves `url` for SSE. Claude Code, Codex, Cursor, and VS Code expect `url`.
3. **Check the endpoint.** It is `https://connect.neetodesk.com/mcp/messages`, with no trailing slash.
4. **Check the syntax.** In JSON a trailing comma or an unclosed brace makes the whole file unreadable, which takes out any other servers too.
Full snippets for each client are on [Connect agent](/mcp/connect).
Go to [chatgpt.com](https://chatgpt.com) and open **Settings → Security and login**. Scroll to the bottom of the page. **Developer mode** is the last section, below **Lockdown mode**.
If **Lockdown mode** is on, turn it off first. ChatGPT does not allow both at the same time. If **Developer mode** is still not there, your ChatGPT workspace admin has turned it off. Ask them to allow it, then follow the **ChatGPT web app** steps in [Connect agent](/mcp/connect).
The ChatGPT desktop app does not need **Developer mode**. Follow the **ChatGPT desktop app** steps in [Connect agent](/mcp/connect) instead.
On an API key connection, the `Authorization` header must read `Bearer YOUR_API_KEY`, with the word `Bearer` and a single space before the key. Confirm the key has not been revoked, and that it belongs to the workspace you expect: a key generated in one workspace does not work in another. Generate a new key from [workspace settings](https://help.neetodesk.com/articles/api-keys) if you are unsure.
On an OAuth connection there is no key to check. Remove the connector and add it again to run the sign-in from the start.
A client that finds a credential in its config never starts the OAuth flow. Remove the `headers` block, or the `bearer_token_env_var` line in Codex, and restart the client. It then answers the server's 401 by opening the sign-in.
The opposite case: the credential is missing, so the client falls back to OAuth. Add the `headers` block with `"Authorization": "Bearer YOUR_API_KEY"`, or set `bearer_token_env_var = "NEETODESK_API_KEY"` in Codex and export that variable, then restart the client. Codex takes the *name* of an environment variable, not the key itself.
The first screen needs a workspace subdomain or a workspace URL, not an email address. For `acme.neetodesk.com`, enter `acme`. See [Workspace subdomain](/mcp/workspace-subdomain).
That is the scoping working as intended. An OAuth connection acts as the person who approved it, so lists and searches come back filtered to your permissions, and asking for a specific ticket you cannot open is refused rather than answered.
If the assistant genuinely needs to reach everything in the workspace, that is what an API key connection is for. See [Authentication](/mcp/authentication).
The approval screen has two optional permissions, **Create and update** and **Delete**, and a tool that needs one you left unticked is refused with a message naming it. Nothing is broken and nothing half happened.
To grant it, remove the connector and add it again, ticking the permission this time. See [Authentication](/mcp/authentication#what-you-approve). An API key connection carries every permission, so this cannot happen on one.
An OAuth connection covers the workspaces you ticked while approving it. Name the workspace in your prompt, or ask the assistant to list the workspaces it can reach. To add another one, run the sign-in again and tick it.
An API key belongs to exactly one workspace, so a key connection reaching the wrong one means the wrong key. Add the server a second time, with the other key and a different server name.
MCP needs VS Code 1.99 or later with GitHub Copilot running in **Agent** mode. It is not available in Ask or Edit mode. Check the version, switch Copilot to Agent mode, and confirm that `.vscode/mcp.json` is in the workspace you have open. On an API key connection, VS Code prompts for the key the first time the server is used, so answer that prompt.
Enable the server after saving the config: **Settings → Cascade → MCP Servers** in Windsurf, **Settings → Customizations** in Antigravity. Windsurf also caps the total number of tools at 100 across every connected server, so turn off servers you are not using and reload.
Open **[Customize > Connectors](https://claude.ai/customize/connectors)**, remove NeetoDesk, and connect it again from the [NeetoDesk listing](https://claude.ai/directory/neetodesk).
On a Team plan, **Requested** means an admin has not turned NeetoDesk on yet. An admin turns it on under **[Organization settings > Connectors](https://claude.ai/admin-settings/connectors)**.
Only enabled forms are returned. Check that the form is enabled in your workspace settings.
Drafts are saved on the ticket, unsent, for you to review and send from NeetoDesk. Comments written through this server are stored on the ticket and are never emailed to the customer either.
The server publishes its OAuth metadata without any credential, so one request tells you whether the problem is on the server or in your client:
```bash theme={"system"}
curl -s https://connect.neetodesk.com/.well-known/oauth-authorization-server
```
A JSON document whose `issuer` is `https://connect.neetodesk.com` means the host is reachable and answering, so look at the client's setup next. It does not prove that every tool call succeeds. No answer, or an error page, means the server cannot be reached from your network. Try again in a few minutes, and if it persists, tell us.
## Still stuck
Reach us through the [help center](https://help.neetodesk.com), or at [support@neetodesk.com](mailto:support@neetodesk.com). Tell us which client you are using and what the assistant reports when it tries a NeetoDesk tool.
# Workspace subdomain
Source: https://apidocs.neetodesk.com/mcp/workspace-subdomain
Find your workspace subdomain and use it to connect an assistant.
Every NeetoDesk workspace has its own subdomain. It is the part of your workspace URL before `neetodesk.com`. For `https://spinkart.neetodesk.com`, the subdomain is `spinkart`.
The MCP server URL is the same for every workspace, `https://connect.neetodesk.com/mcp/messages`. The subdomain is how you tell NeetoDesk which workspace the assistant can reach.
## Find your subdomain
Sign in to NeetoDesk in your browser and look at the address bar.
For `https://green-earth.neetodesk.com`, the subdomain is `green-earth`.
## Where you use it
* **OAuth.** The first sign-in screen asks you to connect your workspace. Enter the subdomain, such as `green-earth`. You can also paste the full workspace URL, and only the subdomain is kept. An email address does not work. The screen links to this page as "Find your workspace subdomain".
* **API key.** Create the key in the workspace you want the assistant to reach. One key reaches one workspace.
See [Authentication](/mcp/authentication) for both routes.