# Create comment Source: https://apidocs.neetodesk.com/api-reference-v1/comments/create bundled-v1/comments.yaml POST /comments Creates a comment for a specific ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create draft comment Source: https://apidocs.neetodesk.com/api-reference-v1/comments/create-draft bundled-v1/comments.yaml POST /tickets/{ticket_id}/drafts Creates a draft comment for a specific ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List comments Source: https://apidocs.neetodesk.com/api-reference-v1/comments/list bundled-v1/comments.yaml GET /tickets/{ticket_id}/comments Retrieves all comments for a specific ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create customer Source: https://apidocs.neetodesk.com/api-reference-v1/customers/create bundled-v1/customers.yaml POST /customers Creates a new customer. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get agent performance report Source: https://apidocs.neetodesk.com/api-reference-v1/reports/get-agent-performance bundled-v1/reports.yaml GET /reports/agents This API retrieves performance metrics for all agents, including average response times, resolution times, and ticket counts with comparisons to the previous period. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get customer satisfaction report Source: https://apidocs.neetodesk.com/api-reference-v1/reports/get-customer-satisfaction bundled-v1/reports.yaml GET /reports/surveys This API retrieves customer satisfaction survey results showing response counts for each rating option. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get team performance report Source: https://apidocs.neetodesk.com/api-reference-v1/reports/get-team-performance bundled-v1/reports.yaml GET /reports/groups This API retrieves performance metrics for all teams (groups), including average response times, resolution times, and ticket counts with comparisons to the previous period. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket volume time series Source: https://apidocs.neetodesk.com/api-reference-v1/reports/get-ticket-time-series bundled-v1/reports.yaml GET /reports/tickets/time_series This API retrieves ticket volume data over time in a format suitable for charting, showing daily ticket counts for new and closed tickets. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket volume report Source: https://apidocs.neetodesk.com/api-reference-v1/reports/get-ticket-volume bundled-v1/reports.yaml GET /reports/tickets This API retrieves ticket volume statistics including counts by status and change percentages compared to the previous period. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Add team members Source: https://apidocs.neetodesk.com/api-reference-v1/team-members/add bundled-v1/team-members.yaml POST /team_members Adds team members to the workspace. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List team members Source: https://apidocs.neetodesk.com/api-reference-v1/team-members/list bundled-v1/team-members.yaml GET /team_members Lists all team members in the workspace. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Remove a team member Source: https://apidocs.neetodesk.com/api-reference-v1/team-members/remove bundled-v1/team-members.yaml DELETE /team_members/{team_member_id} Removes a team member from the workspace. The team member is deactivated (soft-deleted). **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Update team member Source: https://apidocs.neetodesk.com/api-reference-v1/team-members/update bundled-v1/team-members.yaml PATCH /team_members/{team_member_id} Updates the details of a team member in the organization. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create ticket Source: https://apidocs.neetodesk.com/api-reference-v1/tickets/create bundled-v1/tickets.yaml POST /tickets Creates a new ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket Source: https://apidocs.neetodesk.com/api-reference-v1/tickets/get bundled-v1/tickets.yaml GET /tickets/{ticket_id} Retrieves details of a specific ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List tickets Source: https://apidocs.neetodesk.com/api-reference-v1/tickets/list bundled-v1/tickets.yaml GET /tickets Retrieves a list of tickets in a workspace. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Update ticket Source: https://apidocs.neetodesk.com/api-reference-v1/tickets/update bundled-v1/tickets.yaml PUT /tickets/{ticket_id} Updates a ticket. **Deprecated:** This is a **v1** endpoint. It will continue to work, but we recommend migrating to the [v2 equivalent](/api) for improved REST compliance (correct HTTP status codes, consistent response envelopes, and hyphenated URLs). Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Upload attachment Source: https://apidocs.neetodesk.com/api/attachments/create bundled/attachments.yaml POST /attachments Uploads a file and returns a `signed_id` for it. Pass that id in `attachments` on [Create ticket](/api/tickets/create) or [Create comment](/api/comments/create) to attach the file. The upload is a two-step flow because a file has no home until the record it belongs to exists. An uploaded file that is never attached is cleaned up automatically. A `signed_id` can only be attached in the workspace that uploaded it. Two different size limits apply, so it is worth knowing both. This endpoint accepts a file up to the workspace upload limit, 50 MB by default. Attaching it to a ticket or comment applies the smaller attachment limit, 10 MB by default, and a file over that is rejected at attach time rather than silently dropped. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Reserve an attachment upload Source: https://apidocs.neetodesk.com/api/attachments/create-upload bundled/attachments.yaml POST /attachment_uploads Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use [Upload attachment](/api/attachments/create) instead to send the file in one request. Three steps: 1. `POST` the file's `filename`, `byte_size` and `checksum` here. The response carries a `signed_id`, an `upload_url` and the `upload_headers` the next step must send. 2. `PUT` the file's bytes to `upload_url`, carrying every header in `upload_headers` unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the `signed_id` in `attachments` on [Create ticket](/api/tickets/create) or [Create comment](/api/comments/create). `checksum` is the base64-encoded MD5 digest of the file, which is the form `Content-MD5` takes - not the hex digest. Generate it with `openssl dgst -md5 -binary report.pdf | base64`. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a `signed_id` whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
## Sending the file The response tells you where to send the bytes. Send them with a `PUT`, carrying every header from `upload_headers` unchanged. ```bash theme={"system"} FILE=$HOME/Downloads/report.pdf # 1. Reserve the upload. `wc -c` pads its output on BSD and macOS, so trim it. curl -X POST "https://.neetodesk.com/api/external/v2/attachment_uploads" \ -H "X-Api-Key: " \ -H "Content-Type: application/json" \ -d "{ \"filename\": \"$(basename "$FILE")\", \"byte_size\": $(wc -c < "$FILE" | tr -d ' '), \"checksum\": \"$(openssl dgst -md5 -binary "$FILE" | base64)\", \"content_type\": \"application/pdf\" }" # 2. Send the bytes to the upload_url from the response. Pass each header from # upload_headers exactly as the response gave it, values included. curl -X PUT "" \ -H "Content-Type: " \ -H "Content-MD5: " \ --data-binary "@$FILE" # 3. Attach it. curl -X POST "https://.neetodesk.com/api/external/v2/tickets" \ -H "X-Api-Key: " \ -H "Content-Type: application/json" \ -d '{ "email": "customer@example.com", "subject": "Printer is jammed", "description": "Report attached.", "attachments": [""] }' ``` `checksum` is the **base64-encoded** MD5 digest, not the hex digest. The storage service verifies it, so a hex digest fails at step 2 rather than at step 1. Send the headers from `upload_headers` rather than composing your own. Their values are covered by the upload signature, so an altered or missing one is refused. Which headers appear depends on how the workspace stores files, so read them from the response rather than hardcoding the set above. # Authentication Source: https://apidocs.neetodesk.com/api/authentication Learn how to authenticate your requests to the NeetoDesk API using API keys. ## Generating an API key To use the NeetoDesk APIs, you must first generate an API key: 1. Go to the **Admin panel** and click **API keys**. 2. Click **Add API key**. The **Add new API key** pane opens. 3. Enter a **Name** for the key. 4. Under **Type**, choose **General**. The **Zapier** type is only for the Zapier integration. 5. Pick an **Expiry date**, or check **Never expires**. 6. Click **Save changes**. 7. Copy the key from the **API keys** list using the copy icon. The key stays in the list, so you can copy it again later. [Learn more about API keys here.](https://help.neetodesk.com/articles/api-keys) ## Making authenticated API requests To authenticate your API requests, include a valid API key in the `X-Api-Key` header. ```bash theme={"system"} curl https://{your-subdomain}.neetodesk.com/api/external/v2/tickets \ --header 'X-Api-Key: your-api-key' \ --header 'Content-Type: application/json' ``` ## Header format Include your API key in the `X-Api-Key` header of every request: ```http theme={"system"} X-Api-Key: your-api-key ``` Generate separate API keys for different workspaces. An API key generated in one workspace will not work in another. API keys provide access to sensitive data. Treat them like passwords and follow security best practices. ## Error responses ### 401 Unauthorized If your API key is missing, invalid, or expired, you'll receive a 401 error: ```json theme={"system"} { "error": "Could not authenticate, invalid API Key" } ``` ### Common authentication issues **Problem**: API key is missing or invalid.
**Solution**: Verify your API key is correct and hasn't been revoked/expired.
**Problem**: Subdomain is left as the default value (**spinkart**) or incorrect.
**Solution**: Verify your subdomain name is properly added in the request URL.
# Create comment Source: https://apidocs.neetodesk.com/api/comments/create bundled/comments.yaml POST /tickets/{ticket_id}/comments Creates a comment for a specific ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create draft comment Source: https://apidocs.neetodesk.com/api/comments/create-draft bundled/comments.yaml POST /tickets/{ticket_id}/drafts Creates a draft comment for a specific ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get a comment Source: https://apidocs.neetodesk.com/api/comments/get bundled/comments.yaml GET /tickets/{ticket_id}/comments/{comment_id} Retrieves a specific comment for a ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List comments Source: https://apidocs.neetodesk.com/api/comments/list bundled/comments.yaml GET /tickets/{ticket_id}/comments Retrieves all comments for a specific ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create customer Source: https://apidocs.neetodesk.com/api/customers/create bundled/customers.yaml POST /customers Creates a new customer. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create ticket field Source: https://apidocs.neetodesk.com/api/fields/create bundled/fields.yaml POST /fields Creates a custom ticket field in the workspace. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Delete ticket field Source: https://apidocs.neetodesk.com/api/fields/delete bundled/fields.yaml DELETE /fields/{field_id} Deletes a custom ticket field along with the values stored against it in tickets. The field must be `inactive` before it can be deleted. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket field Source: https://apidocs.neetodesk.com/api/fields/get bundled/fields.yaml GET /fields/{field_id} Retrieves details of a specific custom ticket field. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List ticket fields Source: https://apidocs.neetodesk.com/api/fields/list bundled/fields.yaml GET /fields Lists the custom ticket fields in the workspace, ordered by their display order. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Update ticket field Source: https://apidocs.neetodesk.com/api/fields/update bundled/fields.yaml PATCH /fields/{field_id} Updates a custom ticket field. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List forms Source: https://apidocs.neetodesk.com/api/forms/list bundled/forms.yaml GET /forms Lists the enabled web forms through which customers raise tickets. Disabled forms are not returned. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Getting the ticket ID Source: https://apidocs.neetodesk.com/api/getting-ticket-id Understand how to retrieve ticket IDs and numbers for API calls and customer communication. ## Overview NeetoDesk uses two types of identifiers for tickets: * **Ticket ID** - A unique UUID-based identifier required for API calls. * **Ticket Number** - A sequential, user-friendly number for display and customer communication. Both identifiers can be used interchangeably in API endpoints that require a ticket identifier. ## What is a ticket ID? A ticket ID is a unique UUID assigned to each ticket in your workspace. It's required for making API requests that interact with specific tickets, such as retrieving ticket details, updating tickets, or adding comments. e.g., `123e4567-e89b-12d3-a456-426614174000` ## What is a ticket number? A ticket number is a sequential identifier displayed in the NeetoDesk interface for each ticket in your workspace. Unlike ticket IDs, ticket numbers are user-friendly sequential numbers like `#1`, `#2`, `#3`, etc., primarily used for display purposes and customer communication. ## Getting ticket identifiers There are two main ways to get ticket identifiers: * **Using the API** - Programmatically retrieve all tickets and their identifiers. * **From the URL** - Extract the number directly from your browser's address bar (ticket number only). ### Method 1: Using the API endpoint The [List all tickets](/api/tickets/list) API endpoint returns both ticket IDs and numbers along with other ticket information. #### Step-by-step instructions 1. **Make the API request** Send a `GET` request to the [List all tickets](/api/tickets/list) API endpoint, including your API key in the request header. 2. **Parse the response** The response will contain an array of tickets, each with both `id` and `number` fields. Example: ```json theme={"system"} { "tickets": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "number": 1, "subject": "Unable to login to my account", "description": "I'm having trouble accessing my account after the recent update.", "priority": "high", "status": "open", "category": "technical_support", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-20T14:45:00Z", "requester": { "id": "user123", "name": "John Doe", "email": "john.doe@example.com" } } // ... rest of the tickets ], "pagination": { "total_records": 1, "total_pages": 1, "current_page_number": 1, "page_size": 1 } } ``` 3. **Extract the ticket identifiers** From this example: * **Ticket ID**: `123e4567-e89b-12d3-a456-426614174000` * **Ticket Number**: `1` You can access both identifiers in your code from the response: ```javascript JavaScript theme={"system"} // Get the first ticket's ID and number const ticketId = response.tickets[0].id; const ticketNumber = response.tickets[0].number; // Or find a specific ticket by subject const loginTicket = response.tickets.find(ticket => ticket.subject.includes("login") ); const loginTicketId = loginTicket.id; const loginTicketNumber = loginTicket.number; ``` ```python Python theme={"system"} # Get the first ticket's ID and number ticket_id = response['tickets'][0]['id'] ticket_number = response['tickets'][0]['number'] # Or find a specific ticket by subject login_ticket = next((ticket for ticket in response['tickets'] if 'login' in ticket['subject'].lower()), None) if login_ticket: login_ticket_id = login_ticket['id'] login_ticket_number = login_ticket['number'] ``` ```bash Bash theme={"system"} # Get the first ticket's ID and number using jq ticket_id=$(echo "$response" | jq -r '.tickets[0].id') ticket_number=$(echo "$response" | jq -r '.tickets[0].number') # Or find a specific ticket by subject login_ticket_id=$(echo "$response" | jq -r '.tickets[] | select(.subject | contains("login")) | .id') login_ticket_number=$(echo "$response" | jq -r '.tickets[] | select(.subject | contains("login")) | .number') ``` The `tickets` array contains all tickets in your workspace. You can filter by subject, status, or other properties to find the specific ticket you need. ### Method 2: From the URL (ticket numbers only) Ticket numbers also appear in NeetoDesk interface URLs, making them easy to identify when viewing tickets in the web interface. For example, in the URL: ``` https://{your-subdomain}.neetodesk.com/admin/tickets/10 ``` The ticket number is: **10** #### Step-by-step instructions 1. **Navigate to your ticket** Log into your NeetoDesk workspace and navigate to the ticket you want to work with. 2. **Locate the ticket URL** Once you're viewing the ticket, look at your browser's address bar. The URL will contain the ticket number. 3. **Extract the ticket number** The ticket number is found at the end of the URL path. Look for a pattern like: ``` https://{your-subdomain}.neetodesk.com/admin/tickets/{ticket-number} ``` The `{ticket-number}` part is what you need. #### URL examples Here are some examples of how ticket numbers appear in URLs: | URL Example | Ticket Number | | - | - | | `https://mycompany.neetodesk.com/admin/tickets/1` | `1` | | `https://support.neetodesk.com/admin/tickets/25` | `25` | | `https://helpdesk.neetodesk.com/admin/tickets/150` | `150` | # NeetoDesk API Source: https://apidocs.neetodesk.com/api/introduction Welcome to the NeetoDesk API documentation. The NeetoDesk API is organized around REST principles and uses standard HTTP response codes. All API endpoints return JSON responses and require authentication. Before you start with NeetoDesk APIs you will need the following: 1. [Workspace subdomain](/api/workspace-subdomain). 2. [API key](/api/authentication). ## Rate limiting API requests are subject to rate limiting to ensure fair use. If you exceed the rate limit, you'll receive a 429 response. If you encounter this error try again after some time. ## Need help? Visit our [help center](https://help.neetodesk.com), email us at [support@neetodesk.com](mailto:support@neetodesk.com), or join the [Neeto Community](https://neeto.com/community). The community is powered by Slack and is the fastest way to get help directly from the developers, designers, and product folks building NeetoDesk. # Pagination Source: https://apidocs.neetodesk.com/api/pagination Learn how to handle paginated responses in the NeetoDesk API. ## Pagination parameters You can control pagination using the following query parameters: The page of results you want to retrieve (starting from 1). The number of items to return per page. ## Example usage Here's how to retrieve paginated results: ```bash cURL theme={"system"} curl --request GET \ --url "https://{your-subdomain}.neetodesk.com/api/external/v2/tickets?page_number=2&page_size=25" \ --header 'X-Api-Key: your-api-key' ``` ```javascript JavaScript theme={"system"} const response = await fetch( "https://{your-subdomain}.neetodesk.com/api/external/v2/tickets?page_number=2&page_size=25", { method: "GET", headers: { "X-Api-Key": "your-api-key", "Content-Type": "application/json", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"system"} import requests url = "https://{your-subdomain}.neetodesk.com/api/external/v2/tickets" params = { "page_number": 2, "page_size": 25 } headers = { "X-Api-Key": "your-api-key", "Content-Type": "application/json" } response = requests.get(url, params=params, headers=headers) data = response.json() print(data) ``` This retrieves the second page of tickets, with 25 results per page. ## Response structure Paginated responses include metadata about the pagination: ```json Response example theme={"system"} { "tickets": [ // ... array of ticket objects ], "pagination": { "total_records": 150, "current_page_number": 2, "total_pages": 6, "page_size": 25 } } ``` The total number of items across all pages. The current page number. The total number of pages available. The number of items per page. ## Default behavior * If you leave out `page_number`, you get the first page. * If you leave out `page_size`, each page has 30 items. ## Best practices 1. **Start with reasonable page sizes**: Use page sizes between 10-100 items for optimal performance. 2. **Handle empty results**: Always check if the returned array is empty to detect the end of data. 3. **Use total\_records**: Use the `pagination.total_records` field to calculate the total number of pages needed. 4. **Implement error handling**: Handle cases where the requested page doesn't exist. # Get agent performance report Source: https://apidocs.neetodesk.com/api/reports/get-agent-performance bundled/reports.yaml GET /reports/agents This API retrieves performance metrics for all agents, including average response times, resolution times, and ticket counts with comparisons to the previous period. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get customer satisfaction report Source: https://apidocs.neetodesk.com/api/reports/get-customer-satisfaction bundled/reports.yaml GET /reports/surveys This API retrieves customer satisfaction survey results showing response counts for each rating option. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get team performance report Source: https://apidocs.neetodesk.com/api/reports/get-team-performance bundled/reports.yaml GET /reports/groups This API retrieves performance metrics for all teams (groups), including average response times, resolution times, and ticket counts with comparisons to the previous period. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket volume time series Source: https://apidocs.neetodesk.com/api/reports/get-ticket-time-series bundled/reports.yaml GET /reports/ticket-time-series This API retrieves ticket volume data over time in a format suitable for charting, showing daily ticket counts for new and closed tickets. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket volume report Source: https://apidocs.neetodesk.com/api/reports/get-ticket-volume bundled/reports.yaml GET /reports/tickets This API retrieves ticket volume statistics including counts by status and change percentages compared to the previous period. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# HTTP response codes Source: https://apidocs.neetodesk.com/api/response-codes Learn about the HTTP status codes returned by the NeetoDesk API. ## Success codes The request was successful and the server returned the requested data. The request was successful and a new resource was created. Returned for POST requests that create new resources. The request was successful but there is no content to return. Typically returned for DELETE operations. ## Error codes There was an issue with the request. This usually indicates malformed JSON, missing required parameters, or invalid parameter values. The request needs to be authenticated. This typically means: * Missing API key. * Invalid API key. * Expired API key. An operation was not allowed. This could mean: * Insufficient permissions for the requested action. * Attempting to access a resource you don't own. * API key doesn't have the required scope. The requested URL or resource was not found. This could indicate: * Incorrect endpoint URL. * Resource ID doesn't exist. * Workspace subdomain is incorrect. The given request could not be processed by the server due to malformed request. Check the request body once again. Rate limit exceeded. Try again after some time. ## Server error codes An unexpected error occurred on the server. If this persists, please contact support. The server does not support the functionality required to fulfill the request. ## Error response format When an error occurs, the API returns a JSON response with error details: ```json 401 Unauthorized theme={"system"} { "error": "Could not authenticate, invalid API Key" } ``` A human-readable error message. When debugging API issues, the HTTP status code often provides the first clue about what went wrong. Start by checking the status code before diving into the response body. # Add team members Source: https://apidocs.neetodesk.com/api/team-members/add bundled/team-members.yaml POST /team-members Adds team members to the workspace. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get team member details Source: https://apidocs.neetodesk.com/api/team-members/get bundled/team-members.yaml GET /team-members/{team_member_id} Retrieves details of a specific team member. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List team members Source: https://apidocs.neetodesk.com/api/team-members/list bundled/team-members.yaml GET /team-members Lists all team members in the workspace. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Remove a team member Source: https://apidocs.neetodesk.com/api/team-members/remove bundled/team-members.yaml DELETE /team-members/{team_member_id} Removes a team member from the workspace. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Update team member Source: https://apidocs.neetodesk.com/api/team-members/update bundled/team-members.yaml PATCH /team-members/{team_member_id} Updates the details of a team member in the organization. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Create ticket Source: https://apidocs.neetodesk.com/api/tickets/create bundled/tickets.yaml POST /tickets Creates a new ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Get ticket Source: https://apidocs.neetodesk.com/api/tickets/get bundled/tickets.yaml GET /tickets/{ticket_id} Retrieves details of a specific ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# List tickets Source: https://apidocs.neetodesk.com/api/tickets/list bundled/tickets.yaml GET /tickets Retrieves a list of tickets in a workspace. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Update ticket Source: https://apidocs.neetodesk.com/api/tickets/update bundled/tickets.yaml PUT /tickets/{ticket_id} Updates a ticket. Replace `{your-subdomain}` with your workspace's subdomain.
Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain).
# Workspace subdomain Source: https://apidocs.neetodesk.com/api/workspace-subdomain Learn how to find your workspace subdomain. ## What is a subdomain? A subdomain is the part of your workspace URL that comes before the main domain. For example, in the URL: ``` https://spinkart.neetodesk.com ``` **spinkart** is the subdomain. This subdomain is crucial for making API requests, as it identifies your specific workspace within NeetoDesk. ## Step-by-step instructions You can usually find this in your browser's address bar when logged into your workspace. Example: ``` https://your-subdomain.neetodesk.com ``` Our main domain is: `neetodesk.com` Everything before `neetodesk.com` is your subdomain. **Example:** * URL: `https://green-earth.neetodesk.com` * Subdomain: `green-earth` ## Using your subdomain in API requests Once you've identified your subdomain, you'll use it in all API requests. The base URL format is: ``` https://{your-subdomain}.neetodesk.com/api/external/v2 ``` For example, if your subdomain is `mycompany`, your API base URL would be: ``` https://mycompany.neetodesk.com/api/external/v2 ``` ## Common examples Here are some common subdomain patterns: | Type | Example URL | Subdomain | | - | - | - | | Company | `https://acmecorp.neetodesk.com` | `acmecorp` | | Department | `https://marketing.neetodesk.com` | `marketing` | | Project | `https://product-launch.neetodesk.com` | `product-launch` | # Attachments Source: https://apidocs.neetodesk.com/cli-reference/attachments Upload a file so a ticket or comment can carry it. An attachment is a file carried by a ticket or a comment. For fields and response details, see the [API reference](/api/attachments/create-upload). Most of the time you do not need this command: `--attach` on [`tickets create`](/cli-reference/tickets#create-a-ticket) and [`tickets comments create`](/cli-reference/tickets#create-a-comment) uploads and attaches a file in one step. ## Upload a file This command uploads one file and prints the `signed_id` that attaches it. Use this when the file has to be attached by a later call than the one that uploads it, such as a script that uploads once and reuses the id, or a request you build yourself against the API. The file is sent straight to storage rather than through the API. ```bash theme={"system"} neetodesk attachments upload ~/Downloads/report.pdf ``` **Required arguments:** * `` - Path to the file to upload This command takes no flags beyond the [global ones](/cli-reference/overview#global-flags). ```json Sample output (--json) theme={"system"} { "data": { "signed_id": "eyJfcmFpbHMiOnsiZGF0YSI6IjEyMyJ9fQ==", "filename": "report.pdf", "content_type": "application/pdf", "byte_size": 20481, "checksum": "Y2hlY2tzdW1leGFtcGxl", "upload_url": "https://neeto-desk.s3.amazonaws.com/3ieihu6erxcialumauhj1pqs07hr?X-Amz-Signature=...", "upload_headers": { "Content-Type": "application/pdf" } } } ``` With `--quiet`, the command prints the payload without the envelope. Read `signed_id` from it, then attach it through the API: ```bash theme={"system"} SIGNED_ID=$(neetodesk attachments upload ~/Downloads/report.pdf --quiet \ | python3 -c 'import json,sys; print(json.load(sys.stdin)["signed_id"])') ``` A `signed_id` is only accepted in the workspace that uploaded it, and a file that is never attached to anything is cleaned up automatically. # Customers Source: https://apidocs.neetodesk.com/cli-reference/customers List, view, and create the customers who raise tickets. A customer is a person who raises tickets. For fields and response details, see the [API reference](/api/customers/create). ## List customers This command lists the customers in your workspace. Use this when you want to find a customer's ID. `--email` filters by an exact email address. ```bash theme={"system"} neetodesk customers list neetodesk customers list --email customer@example.com ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--email` | `string` | | | Filter by exact email | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | ## Show a customer This command shows one customer. Use this when you want to inspect a customer after finding their ID from `customers list`. ```bash theme={"system"} neetodesk customers show ``` **Required arguments:** * `` - Customer ID ## Create a customer This command adds a customer to the workspace. Use this when you want to set a customer's name, phone number, and time zone before their first ticket arrives. The email address is what identifies them, and it is the same address [`tickets create`](/cli-reference/tickets#create-a-ticket) matches on. ```bash theme={"system"} neetodesk customers create \ --email customer@example.com \ --first-name Sam \ --last-name Smith \ --time-zone America/New_York ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--company-id` | `string` | | | Company ID to associate | | `--description` | `string` | | | Customer description | | `--email` | `string` | Yes | | Customer email | | `--first-name` | `string` | | | Customer first name | | `--language` | `string` | | | Preferred language | | `--last-name` | `string` | | | Customer last name | | `--phone` | `string` | | | Customer phone number | | `--time-zone` | `string` | | | Time zone (e.g. America/New\_York) | ```json Sample output (--json) theme={"system"} { "data": { "customer": { "id": "c4d5e6f7-8a9b-40c1-b2d3-e4f5a6b7c8d9", "name": "Sam Smith", "email": "customer@example.com" } } } ``` The CLI has no command for updating customers. # Forms Source: https://apidocs.neetodesk.com/cli-reference/forms List the web forms through which customers raise tickets. A form is a web form your workspace publishes for customers to raise tickets through. For fields and response details, see the [API reference](/api/forms/list). ## List forms This command lists the workspace's enabled forms with the slug each one is published under. Use this when you want to find the form a customer uses. Disabled forms are left out, and so is the `kb_details` block the API returns alongside the forms. ```bash theme={"system"} neetodesk forms list ``` This command takes no flags beyond the [global ones](/cli-reference/overview#global-flags). ```json Sample output (--json) theme={"system"} { "data": [ { "id": "9c8b7a65-4d3e-42f1-a0b9-8c7d6e5f4a3b", "title": "Contact support", "slug": "contact-support" } ], "breadcrumbs": [ { "label": "View", "command": "neetodesk forms list" } ] } ``` # Commands overview Source: https://apidocs.neetodesk.com/cli-reference/overview Every neetodesk command grouped by resource, with links to the full reference. ## Global flags These flags work on every command and are left out of the per-command flag tables below. See [Output formats](/cli/output-formats) for details. | Flag | Description | | - | - | | `--json` | Output as JSON | | `--quiet` | Output raw data only (no envelope) | | `--toon` | Output in TOON format (token-optimized for AI agents) | | `--subdomain` | Override saved subdomain | ### attachments Upload files to attach to tickets and comments | Command | Description | | - | - | | [`neetodesk attachments upload`](/cli-reference/attachments) | Upload a file and print the signed id that attaches it | ### customers Manage customers | Command | Description | | - | - | | [`neetodesk customers create`](/cli-reference/customers) | Create a customer | | [`neetodesk customers list`](/cli-reference/customers) | List customers | | [`neetodesk customers show`](/cli-reference/customers) | Show a customer | ### doctor Check CLI health and connectivity | Command | Description | | - | - | | [`neetodesk doctor`](/cli-reference/utility) | Check CLI health and connectivity | ### forms List contact forms | Command | Description | | - | - | | [`neetodesk forms list`](/cli-reference/forms) | List enabled contact forms | ### login Authenticate to NeetoDesk via browser | Command | Description | | - | - | | [`neetodesk login`](/cli-reference/utility) | Authenticate to NeetoDesk via browser | ### logout Sign out and clear saved credentials | Command | Description | | - | - | | [`neetodesk logout`](/cli-reference/utility) | Sign out and clear saved credentials | ### reports View NeetoDesk reports | Command | Description | | - | - | | [`neetodesk reports agents`](/cli-reference/reports) | Agent performance report | | [`neetodesk reports groups`](/cli-reference/reports) | Group performance report | | [`neetodesk reports surveys`](/cli-reference/reports) | Customer satisfaction surveys report | | [`neetodesk reports ticket-time-series`](/cli-reference/reports) | Ticket creation and closure time series report | | [`neetodesk reports tickets`](/cli-reference/reports) | Ticket status change report | ### setup Set up NeetoDesk for AI coding assistants | Command | Description | | - | - | | [`neetodesk setup claude`](/cli-reference/utility) | Register NeetoDesk plugin with Claude Code | | [`neetodesk setup codex`](/cli-reference/utility) | Add NeetoDesk instructions for OpenAI Codex | | [`neetodesk setup copilot`](/cli-reference/utility) | Add NeetoDesk instructions for GitHub Copilot | | [`neetodesk setup cursor`](/cli-reference/utility) | Write NeetoDesk rules for Cursor IDE | | [`neetodesk setup gemini`](/cli-reference/utility) | Add NeetoDesk instructions for Gemini CLI | | [`neetodesk setup windsurf`](/cli-reference/utility) | Write NeetoDesk rules for Windsurf IDE | ### team-members Manage team members (agents) | Command | Description | | - | - | | [`neetodesk team-members create`](/cli-reference/team-members) | Invite team members by email | | [`neetodesk team-members delete`](/cli-reference/team-members) | Remove a team member | | [`neetodesk team-members list`](/cli-reference/team-members) | List team members | | [`neetodesk team-members show`](/cli-reference/team-members) | Show a team member | | [`neetodesk team-members update`](/cli-reference/team-members) | Update a team member | ### tickets Manage tickets | Command | Description | | - | - | | [`neetodesk tickets comments create`](/cli-reference/tickets) | Add a comment to a ticket | | [`neetodesk tickets comments list`](/cli-reference/tickets) | List comments on a ticket | | [`neetodesk tickets comments show`](/cli-reference/tickets) | Show a comment on a ticket | | [`neetodesk tickets create`](/cli-reference/tickets) | Create a ticket | | [`neetodesk tickets drafts create`](/cli-reference/tickets) | Create or upsert a draft on a ticket | | [`neetodesk tickets list`](/cli-reference/tickets) | List tickets | | [`neetodesk tickets show`](/cli-reference/tickets) | Show a ticket by number or id | | [`neetodesk tickets update`](/cli-reference/tickets) | Update a ticket by number or id | ### update Update the CLI to the latest version | Command | Description | | - | - | | [`neetodesk update`](/cli-reference/utility) | Update the CLI to the latest version | ### version Print the CLI version | Command | Description | | - | - | | [`neetodesk version`](/cli-reference/utility) | Print the CLI version | ### whoami Show current authenticated user(s) | Command | Description | | - | - | | [`neetodesk whoami`](/cli-reference/utility) | Show current authenticated user(s) | # Reports Source: https://apidocs.neetodesk.com/cli-reference/reports Show ticket volume, ticket time series, agent and group performance, and survey results. A report aggregates ticket activity over a date range. For fields and response details, see the [API reference](/api/reports/get-ticket-volume). ## Choose the date range Every report takes the same three range flags. Pass `--range-type` for a named window, such as `last_7_days` or `last_30_days`. Pass `--range-type custom` together with `--start-date` and `--end-date` for an exact one: ```bash theme={"system"} neetodesk reports tickets --range-type last_30_days neetodesk reports tickets --range-type custom --start-date 2026-07-01 --end-date 2026-07-31 ``` Dates are `YYYY-MM-DD`. Every figure that is compared against the previous period of the same length comes back as `present`, `previous`, and `change_percentage`. ## Show ticket volume This command reports how many tickets entered each status over the range. Use this when you want a volume snapshot. The pretty output is a table with one row per status. ```bash theme={"system"} neetodesk reports tickets --range-type last_30_days ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | ```json Sample output (--json) theme={"system"} { "data": [ { "name": "new", "value": { "present": 120, "previous": 98, "change_percentage": 22.45 } }, { "name": "closed", "value": { "present": 87, "previous": 91, "change_percentage": -4.4 } } ] } ``` ## Show the ticket time series This command reports tickets opened and tickets closed on each day of the range, as two series aligned to the same list of dates. Use this when you want to plot a trend rather than a single total. The pretty output is a table with one row per date. ```bash theme={"system"} neetodesk reports ticket-time-series --range-type last_7_days ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | ```json Sample output (--json) theme={"system"} { "data": { "dates": ["2026-08-01", "2026-08-02", "2026-08-03"], "data": [ { "name": "new", "values": [12, 9, 14] }, { "name": "closed", "values": [8, 11, 10] } ] } } ``` ## Show agent performance This command reports each agent's average first-response time, average resolution time, and assigned and closed ticket counts, each compared against the previous period of the same length. Use this when you review how each agent is doing. Times are in seconds. The `summary` and `total_count` the API returns alongside the list are not part of the CLI output. ```bash theme={"system"} neetodesk reports agents --range-type last_30_days ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "name": "Oliver Smith", "first_response_time": { "present": 1820.0, "previous": 2140.0, "change_percentage": -14.95 }, "resolution_time": { "present": 16400.0, "previous": 15200.0, "change_percentage": 7.89 }, "assigned": { "present": 42, "previous": 38, "change_percentage": 10.53 }, "closed": { "present": 39, "previous": 35, "change_percentage": 11.43 } } ] } ``` ## Show group performance This command reports the same metrics as `reports agents`, aggregated per group instead of per agent, with each group's `id` and `name`. Use this when you compare teams. ```bash theme={"system"} neetodesk reports groups --range-type last_30_days ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "a29d5c31-8e42-4f16-b73a-1d9c4e6f8a02", "name": "Payments", "first_response_time": { "present": 1540.0, "previous": 1780.0, "change_percentage": -13.48 }, "resolution_time": { "present": 14200.0, "previous": 15600.0, "change_percentage": -8.97 }, "assigned": { "present": 65, "previous": 58, "change_percentage": 12.07 }, "closed": { "present": 61, "previous": 54, "change_percentage": 12.96 } } ] } ``` ## Show survey results This command reports how customers answered each satisfaction survey over the range, as a count per answer choice. Use this when you want to track customer satisfaction. ```bash theme={"system"} neetodesk reports surveys --range-type last_30_days ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e", "name": "Post-resolution survey", "question": "How satisfied are you with the support you received?", "choices": [ { "name": "Satisfied", "count": 31, "slug": "satisfied" }, { "name": "Unsatisfied", "count": 4, "slug": "unsatisfied" } ] } ] } ``` `reports agents` and `reports groups` accept `--page` and `--page-size`. `reports tickets`, `reports ticket-time-series` and `reports surveys` return the whole range and do not paginate. # Team members Source: https://apidocs.neetodesk.com/cli-reference/team-members List, view, add, update, and delete the agents in your workspace. A team member is an agent or another user of the workspace. For fields and response details, see the [API reference](/api/team-members/list). ## List team members This command lists the workspace's active team members. Use this when you want to see who already has access. `--email` matches an address exactly. ```bash theme={"system"} neetodesk team-members list --page-size 50 neetodesk team-members list --email oliver.smith@example.com ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--email` | `string` | | | Filter by email | | `--order` | `string` | | `desc` | Sort direction: asc or desc | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | | `--sort` | `string` | | `created_at` | Sort field: created\_at, updated\_at, email, first\_name or last\_name | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "email": "oliver.smith@example.com", "first_name": "Oliver", "last_name": "Smith", "time_zone": "Asia/Kolkata", "profile_image_url": null, "organization_role": "Admin", "active": true } ], "breadcrumbs": [ { "label": "Show", "command": "neetodesk team-members show " }, { "label": "Update", "command": "neetodesk team-members update " } ], "pagination": { "total_records": 12, "total_pages": 1, "current_page_number": 1, "page_size": 50 } } ``` ## Show a team member This command shows one team member's email address, name, time zone, and role. Use this when you want to inspect a member after finding their ID from `team-members list`. The record has the same fields as a `team-members list` entry and sits under a `team_member` key in `data`. ```bash theme={"system"} neetodesk team-members show 7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c ``` **Required arguments:** * `` - Team member UUID ## Add team members This command invites one or more people to the workspace. Use this when you want to give someone access. Repeat `--email` to invite several at once. Each address is emailed an invitation unless you pass `--send-invitation-email=false`. ```bash theme={"system"} neetodesk team-members create \ --email sam.smith@example.com \ --email jane.doe@example.com \ --role Agent ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--email` | `stringArray` | Yes | `[]` | Email addresses to invite (repeat flag for multiple) | | `--role` | `string` | Yes | | Organization role (e.g. agent, admin) | | `--send-invitation-email` | `bool` | | `true` | Send invitation email to new members | `--role` must name a role that exists under **Roles** in your workspace settings. The API confirms the invitations with a message rather than returning the new members, so this is the one `create` command whose `--quiet` output is that message object rather than an id. Run `team-members list --email` afterwards to fetch a new member's id. ```json Sample output (--json) theme={"system"} { "data": { "message": "Users added successfully" } } ``` ## Update a team member This command changes a team member's email address, name, time zone, or role. Use this when a member's details or role change. Pass only the flags you want to change. The updated record comes back under a `team_member` key in `data`. ```bash theme={"system"} neetodesk team-members update 7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c --role Admin ``` **Required arguments:** * `` - Team member UUID | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--email` | `string` | | | New email | | `--first-name` | `string` | | | First name | | `--last-name` | `string` | | | Last name | | `--role` | `string` | | | Organization role | | `--time-zone` | `string` | | | Time zone | ```json Sample output (--json) theme={"system"} { "data": { "team_member": { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "email": "oliver.smith@example.com", "first_name": "Oliver", "last_name": "Smith", "time_zone": "Asia/Kolkata", "profile_image_url": null, "organization_role": "Admin", "active": true } } } ``` ## Delete a team member This command deactivates a team member, ending their access to the workspace. Use this when someone should no longer have access. Their user record is kept, so the tickets and comments they handled stay attributed to them. The last admin of a workspace cannot be removed. ```bash theme={"system"} neetodesk team-members delete 7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c ``` **Required arguments:** * `` - Team member UUID ```text Sample output theme={"system"} Team member removed. ``` Under `--json` that is `{"message": "Team member removed."}`; under `--quiet` it is `success`. # Tickets Source: https://apidocs.neetodesk.com/cli-reference/tickets List, view, create, and update tickets, and manage their comments and drafts. A ticket is the central work item in NeetoDesk. For fields and response details, see the [API reference](/api/tickets/list). Everywhere a ticket ID is taken, the CLI accepts either the ticket's UUID or its workspace-scoped number, so `neetodesk tickets show 1042` and `neetodesk tickets show 0f1e4c9a-6d3b-4f92-8a71-25b0c8d4e6f3` do the same thing. The number is the one shown in the web app, and is usually the easier of the two to type. ## List tickets This command lists the tickets you can access. Use this when you want to triage a queue. Narrow it to the statuses you care about with a comma-separated `--status`. ```bash theme={"system"} neetodesk tickets list --status open,pending --page-size 25 ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--status` | `string` | | | Filter by status (comma-separated, e.g. open,pending) | | `--customer-id` | `string` | | | Filter by customer ID | | `--customer-email` | `string` | | | Filter by customer email | | `--assignee-id` | `string` | | | Filter by assignee ID | | `--assignee-email` | `string` | | | Filter by assignee email | | `--field-name` | `string` | | | Custom ticket field to filter by, used with --field-value | | `--field-value` | `string` | | | Value the custom ticket field must have, used with --field-name | | `--range-type` | `string` | | | Date range type (e.g. last\_7\_days, last\_30\_days, custom) | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--sort` | `string` | | `created_at` | Sort field: created\_at or updated\_at | | `--order` | `string` | | `desc` | Sort direction: asc or desc | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "0f1e4c9a-6d3b-4f92-8a71-25b0c8d4e6f3", "number": 1042, "subject": "Refund not received", "description": "The card was charged twice on 2 August.", "priority": "high", "status": "open", "category": "Billing", "sub_category_one": null, "sub_category_two": null, "created_at": "2026-08-03T09:14:22.000Z", "updated_at": "2026-08-03T11:02:41.000Z", "external_id": null, "source": null, "group": "Payments", "tags": ["refund"], "assignee": { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "name": "Oliver Smith", "email": "oliver.smith@example.com" }, "customer": { "id": "c4d5e6f7-8a9b-40c1-b2d3-e4f5a6b7c8d9", "name": "Sam Smith", "email": "customer@example.com", "phone_numbers": [] }, "fields": [] } ], "breadcrumbs": [ { "label": "Show", "command": "neetodesk tickets show " }, { "label": "Update", "command": "neetodesk tickets update " } ], "pagination": { "total_records": 137, "total_pages": 6, "current_page_number": 1, "page_size": 25 } } ``` `assignee` and `customer` are left out when the ticket has none, and `fields` lists the custom ticket fields as `field_name` and `field_value` pairs. ## Show a ticket This command shows one ticket in full, including its description and the agent, customer, group, and tags it is associated with. Use this when you want to read a ticket before you act on it. The record has the same fields as a `tickets list` entry and sits under a `ticket` key in `data`. ```bash theme={"system"} neetodesk tickets show 1042 ``` **Required arguments:** * `` - Ticket number or UUID ## Create a ticket This command opens a ticket on behalf of a customer, identified by their email address. Use this when a request reaches you outside NeetoDesk. The description becomes the ticket's first comment. If no customer with that email exists, one is created. ```bash theme={"system"} neetodesk tickets create \ --email customer@example.com \ --subject "Refund not received" \ --description "The card was charged twice on 2 August." \ --priority high \ --assignee-email oliver.smith@example.com ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--agent-id` | `string` | | | Agent ID to assign | | `--assignee-email` | `string` | | | Agent email to assign | | `--attach` | `stringArray` | | `[]` | Path to a file to attach (repeat the flag for more than one) | | `--category` | `string` | | | Ticket category | | `--description` | `string` | Yes | | Ticket description | | `--email` | `string` | Yes | | Customer email | | `--group-id` | `string` | | | Group ID to assign | | `--name` | `string` | | | Customer name | | `--priority` | `string` | | | Ticket priority (low, medium, high, urgent) | | `--status` | `string` | | | Ticket status | | `--subject` | `string` | Yes | | Ticket subject | `--attach` attaches a file to the ticket's first comment. Repeat the flag for more than one file. Every file is uploaded before the ticket is created, so a file that cannot be read or uploaded fails the command and creates nothing. The CLI does not delete the files it had already uploaded before the failure; they are left unattached and reclaimed automatically, so re-running the command is safe. ```bash theme={"system"} neetodesk tickets create \ --email customer@example.com \ --subject "Printer is jammed" \ --description "Photo and log attached." \ --attach ~/Desktop/printer.png \ --attach ~/Desktop/error-log.txt ``` The response carries only the new ticket's identifiers and its URL in the web app. With `--quiet` just the `id` is printed. ```json Sample output (--json) theme={"system"} { "data": { "ticket": { "id": "0f1e4c9a-6d3b-4f92-8a71-25b0c8d4e6f3", "number": 1042, "url": "https://acme.neetodesk.com/admin/tickets/1042" } }, "breadcrumbs": [ { "label": "Show", "command": "neetodesk tickets show " } ] } ``` ## Update a ticket This command changes a ticket's subject, description, status, priority, category, assignee, or group, and adds files with `--attach`. Use this when you triage or resolve a ticket. Pass only the flags you want to change; the rest are left as they are. The updated ticket comes back in full under a `ticket` key in `data`. ```bash theme={"system"} neetodesk tickets update 1042 --status resolved --priority low neetodesk tickets update 1042 --attach ~/Desktop/replacement-part.pdf ``` **Required arguments:** * `` - Ticket number or UUID `--attach` adds each file to the ticket's opening message, beside the files already there, and repeats for more than one file. A ticket that has no opening message cannot take files this way; add them with [`tickets comments create --attach`](#create-a-comment) instead. Attaching applies the workspace attachment limit, 10 MB by default, which is smaller than the 50 MB upload limit, so a file between the two uploads and is then refused. | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--agent-id` | `string` | | | Agent ID to assign | | `--assignee-email` | `string` | | | Agent email to assign | | `--attach` | `stringArray` | | `[]` | Path to a file to attach (repeat the flag for more than one) | | `--category` | `string` | | | Ticket category | | `--description` | `string` | | | Ticket description | | `--group-id` | `string` | | | Group ID to assign | | `--priority` | `string` | | | Ticket priority | | `--status` | `string` | | | Ticket status | | `--subject` | `string` | | | Ticket subject | ```json Sample output (--json) theme={"system"} { "data": { "ticket": { "id": "0f1e4c9a-6d3b-4f92-8a71-25b0c8d4e6f3", "number": 1042, "subject": "Refund not received", "description": "The card was charged twice on 2 August.", "priority": "low", "status": "resolved", "category": "Billing", "sub_category_one": null, "sub_category_two": null, "created_at": "2026-08-03T09:14:22.000Z", "updated_at": "2026-08-03T11:02:41.000Z", "external_id": null, "source": null, "group": "Payments", "tags": ["refund"], "assignee": { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "name": "Oliver Smith", "email": "oliver.smith@example.com" }, "customer": { "id": "c4d5e6f7-8a9b-40c1-b2d3-e4f5a6b7c8d9", "name": "Sam Smith", "email": "customer@example.com", "phone_numbers": [] }, "fields": [] } } } ``` ## List comments This command lists a ticket's comments in the order they were added, each with its type, author, and body. Use this when you want to read the conversation on a ticket. ```bash theme={"system"} neetodesk tickets comments list 1042 ``` **Required arguments:** * `` - Ticket number or UUID | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "3a9d7e21-4b8c-4d15-9e73-6f2a1b0c8d94", "created_at": "2026-08-03T11:02:41.000Z", "comment_type": "reply", "content": "

We have issued the refund.

", "author": { "id": "7b2c1d40-5e6f-4a81-9c23-0d4e5f6a7b8c", "name": "Oliver Smith", "email": "oliver.smith@example.com", "type": "agent" }, "attachments": [] } ], "breadcrumbs": [ { "label": "Show", "command": "neetodesk tickets comments show 1042 " } ], "pagination": { "total_records": 3, "total_pages": 1, "current_page_number": 1, "page_size": 30 } } ``` A comment the system wrote has an `author` of type `system` with no id or email. Each entry in `attachments` carries the file's `id`, `filename`, and `url`. ## Show a comment This command shows one comment on a ticket, under a `comment` key in `data`. Use this when you want a single comment after finding its ID from `tickets comments list`. ```bash theme={"system"} neetodesk tickets comments show 1042 3a9d7e21-4b8c-4d15-9e73-6f2a1b0c8d94 ``` **Required arguments:** * `` - Ticket number or UUID * `` - Comment UUID ## Create a comment This command adds a comment to a ticket. Use this when you want to answer the customer or leave an internal note. A `reply` is published on the ticket where the customer reads it; a `note` stays internal to the workspace. The comment is attributed to you, the signed-in user. The new comment comes back under a `comment` key in `data`, and `--quiet` prints just its `id`. ```bash theme={"system"} neetodesk tickets comments create 1042 \ --content "

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 | NeetoDesk 'Authorize access' screen showing the workspace and signed-in email, a 'What this connection can do' list with Read and Stay connected always granted and Create and update and Delete as tickable boxes, a 'Workspaces to connect' list, and Cancel and Authorize buttons 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. The NeetoDesk entry in Claude's connector directory, with a one-line description and a Connect to Claude button 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**. Menu under the Add button in Claude's Connectors settings, with the single item Add custom connector 2. Enter the name NeetoDesk and paste the server URL. ``` https://connect.neetodesk.com/mcp/messages ``` Claude's Add custom connector dialog with the name NeetoDesk and the server URL https://connect.neetodesk.com/mcp/messages filled in, and a Continue button 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's Developer mode setting, tagged Elevated risk, with its toggle switched on 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**. The profile menu in the ChatGPT desktop app, with Settings in the list 2. Click **Plugins**. Click **Add**, then click **Add MCP server**. The Plugins page in the ChatGPT desktop app, with the Add menu open and Add MCP server in the list 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**. The Connect to a custom MCP form with the name NeetoDesk, the type Streamable HTTP, the server URL filled in, and a Save button 6. Click the **MCPs** tab. Next to **neetodesk**, click **Authenticate**. The neetodesk row in the MCPs list, with an Authenticate button 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. NeetoDesk dialog headed 'Connect your workspace' with a field for the workspace subdomain, a .neetodesk.com suffix, and a Continue button 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. NeetoDesk 'Authorize access' screen showing the workspace and signed-in email, a 'What this connection can do' list with Read and Stay connected always granted and Create and update and Delete as tickable boxes, a 'Workspaces to connect' list with three workspaces, and Cancel and Authorize buttons 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.