# 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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). > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!WARNING] > > **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). > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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": [""] }' ``` > [!WARNING] > > `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. > [!NOTE] > > 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 ``` > [!TIP] > > Generate separate API keys for different workspaces. An API key generated in > one workspace will not work in another. > [!WARNING] > > 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 > **Invalid API key** > > **Problem**: API key is missing or invalid. \\ > > **Solution**: Verify your API key is correct and hasn't been revoked/expired. > **Incorrect workspace subdomain** > > **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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > Replace `{your-subdomain}` with your workspace's subdomain. \ > Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain). # Get customer Source: https://apidocs.neetodesk.com/api/customers/get bundled/customers.yaml GET /customers/{customer_id} Retrieves details of a specific customer. > [!NOTE] > > Replace `{your-subdomain}` with your workspace's subdomain. \ > Learn how to find your subdomain in [Workspace subdomain](/api/workspace-subdomain). # List customers Source: https://apidocs.neetodesk.com/api/customers/list bundled/customers.yaml GET /customers Lists the customers in the workspace, oldest first. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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') ``` > [!NOTE] > > 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: **page\_number** `integer` The page of results you want to retrieve (starting from 1). **page\_size** `integer` 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 } } ``` **pagination.total\_records** `integer` The total number of items across all pages. **pagination.current\_page\_number** `integer` The current page number. **pagination.total\_pages** `integer` The total number of pages available. **pagination.page\_size** `integer` The number of items per page. ## Default behavior > [!NOTE] > > * 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. When called with a CLI session instead of an API key, the report covers only the tickets the signed-in agent is allowed to see. Every response also carries `start_date`, `end_date`, `previous_start_date` and `previous_end_date`; each `previous` value is for the previous period of the same length (for example, `this_month` is compared with all of last month). > [!NOTE] > > 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. When called with a CLI session instead of an API key, the report covers only the tickets the signed-in agent is allowed to see. Every response also carries `start_date`, `end_date`, `previous_start_date` and `previous_end_date`; each `previous` value is for the previous period of the same length (for example, `this_month` is compared with all of last month). > [!NOTE] > > 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. When called with a CLI session instead of an API key, the report covers only the tickets the signed-in agent is allowed to see. Every response also carries `start_date`, `end_date`, `previous_start_date` and `previous_end_date`; each `previous` value is for the previous period of the same length (for example, `this_month` is compared with all of last month). > [!NOTE] > > 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. When called with a CLI session instead of an API key, the report covers only the tickets the signed-in agent is allowed to see. Every response also carries `start_date`, `end_date`, `previous_start_date` and `previous_end_date`; each `previous` value is for the previous period of the same length (for example, `this_month` is compared with all of last month). > [!NOTE] > > 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. When called with a CLI session instead of an API key, the report covers only the tickets the signed-in agent is allowed to see. Every response also carries `start_date`, `end_date`, `previous_start_date` and `previous_end_date`; each `previous` value is for the previous period of the same length (for example, `this_month` is compared with all of last month). > [!NOTE] > > 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 **200 OK** The request was successful and the server returned the requested data. **201 Created** The request was successful and a new resource was created. Returned for POST requests that create new resources. **204 No Content** The request was successful but there is no content to return. Typically returned for DELETE operations. ## Error codes **400 Bad Request** There was an issue with the request. This usually indicates malformed JSON, missing required parameters, or invalid parameter values. **401 Unauthorized** The request needs to be authenticated. This typically means: * Missing API key. * Invalid API key. * Expired API key. **403 Forbidden** 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. **404 Not Found** The requested URL or resource was not found. This could indicate: * Incorrect endpoint URL. * Resource ID doesn't exist. * Workspace subdomain is incorrect. **422 Unprocessable Entity** The given request could not be processed by the server due to malformed request. Check the request body once again. **429 Too Many Requests** Rate limit exceeded. Try again after some time. ## Server error codes **500 Internal Server Error** An unexpected error occurred on the server. If this persists, please contact support. **501 Not Implemented** 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" } ``` **error** `string` A human-readable error message. > [!TIP] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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. > [!NOTE] > > 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, newest first unless `sort` and `order` say otherwise. With an API key the list covers every ticket in the workspace. With a CLI session it covers only the tickets the signed-in agent is allowed to see. > [!NOTE] > > 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. Setting `status` to `moved` is refused. `tags` replaces every tag on the ticket, so use `add_tags` and `remove_tags` to change tags without losing the others; `tags` cannot be combined with either of them. > [!NOTE] > > 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 **1. Locate your workspace URL** You can usually find this in your browser's address bar when logged into your workspace. Example: ``` https://your-subdomain.neetodesk.com ``` **2. Identify the main domain** Our main domain is: `neetodesk.com` **3. Extract the part before the main domain** 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 [List customers](/api/customers/list), [Get customer](/api/customers/get) and [Create customer](/api/customers/create) in the API reference. ## 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` | `stringArray` | Yes | `[]` | Customer email, first is primary (repeat flag for multiple) | | `--first-name` | `string` | | | Customer first name | | `--language` | `string` | | | Preferred language | | `--last-name` | `string` | | | Customer last name | | `--link` | `stringArray` | | `[]` | Customer link (repeat flag for multiple) | | `--phone` | `stringArray` | | `[]` | Customer phone number (repeat flag for multiple) | | `--time-zone` | `string` | | | Time zone (e.g. America/New\_York) | `--email`, `--phone` and `--link` are repeatable. The first `--email` is the primary address. ```bash theme={"system"} neetodesk customers create \ --email customer@example.com \ --email sam.smith@example.org \ --phone "+1 555 0100" ``` ```json Sample output (--json) theme={"system"} { "data": { "customer": { "id": "c4d5e6f7-8a9b-40c1-b2d3-e4f5a6b7c8d9", "name": "Sam Smith", "email": "customer@example.com", "language": "English", "time_zone": "America/New_York", "description": "", "created_at": "2026-09-01T10:15:30.000Z", "updated_at": "2026-09-01T10:15:30.000Z", "emails": ["customer@example.com"], "phone_numbers": [] } } } ``` The CLI has no command for updating customers. # Fields Source: https://apidocs.neetodesk.com/cli-reference/fields List, view, create, update, and delete the custom fields on tickets. A field is a custom field that tickets carry, such as a browser or an order number. For fields and response details, see the [API reference](/api/fields/list). To set a ticket's value for a field, pass `--field Name=Value` to [`tickets create`](/cli-reference/tickets#create-a-ticket) or [`tickets update`](/cli-reference/tickets#update-a-ticket). ## List fields This command lists the workspace's custom ticket fields in display order. Use this when you want to see which fields exist and find a field's ID. `--state` narrows the list to `active` or `inactive` fields. ```bash theme={"system"} neetodesk fields list neetodesk fields list --state active ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | | `--state` | `string` | | | Filter by state: active or inactive | ```json Sample output (--json) theme={"system"} { "data": [ { "id": "aaaabbbb-cccc-dddd-eeee-ffff00003333", "name": "Browser", "kind": "single_option", "state": "active", "resource_type": "ticket", "is_required": false, "is_required_for_agent_when_submitting_form": false, "is_required_for_agent_when_closing_ticket": false, "display_order": 3, "placeholder_slug": "browser", "created_at": "2026-08-03T09:14:22.000Z", "updated_at": "2026-08-03T09:14:22.000Z", "options": [ { "id": "aaaabbbb-cccc-dddd-eeee-ffff00004444", "label": "Chrome", "display_order": 0 } ] } ], "breadcrumbs": [ { "label": "Show", "command": "neetodesk fields show " }, { "label": "Update", "command": "neetodesk fields update " } ], "pagination": { "total_records": 1, "total_pages": 1, "current_page_number": 1, "page_size": 15 } } ``` ## Show a field This command shows one field with its type, state, required flags, and dropdown options. Use this when you need the option IDs before you rename or remove an option. The record sits under a `field` key in `data`. ```bash theme={"system"} neetodesk fields show aaaabbbb-cccc-dddd-eeee-ffff00003333 ``` **Required arguments:** * `` - Field UUID ## Create a field This command creates a custom ticket field. Use this when tickets need a new piece of structured information. `--kind` is one of `text`, `textarea`, `integer`, `decimal`, `date`, `checkbox`, `single_option`, or `multi_option`, and cannot be changed later. Dropdown kinds (`single_option` and `multi_option`) need at least one `--option`; repeat the flag for more. ```bash theme={"system"} neetodesk fields create \ --name Browser \ --kind single_option \ --option Chrome \ --option Firefox \ --required-when-closing ``` | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--display-order` | `int` | | `0` | Position of the field in the ticket form | | `--kind` | `string` | Yes | | Field kind: text, textarea, integer, decimal, date, checkbox, single\_option, multi\_option | | `--name` | `string` | Yes | | Field name | | `--option` | `stringArray` | | `[]` | Option label for single\_option and multi\_option fields (repeat flag for multiple) | | `--placeholder-slug` | `string` | | | Placeholder slug for the field | | `--required` | `bool` | | `false` | Make the field required | | `--required-when-closing` | `bool` | | `false` | Require agents to fill the field when closing a ticket | | `--required-when-submitting-form` | `bool` | | `false` | Require agents to fill the field when submitting the form | | `--state` | `string` | | | Field state: active or inactive | The new field comes back under a `field` key in `data`. ## Update a field This command changes a field's name, state, required flags, display order, or placeholder, and adds, renames, or removes dropdown options. Use this when a field needs adjusting. Pass only the flags you want to change. To delete a field later, first set it to inactive with `--state inactive`. The updated field comes back under a `field` key in `data`. ```bash theme={"system"} neetodesk fields update aaaabbbb-cccc-dddd-eeee-ffff00003333 --add-option Safari neetodesk fields update aaaabbbb-cccc-dddd-eeee-ffff00003333 \ --rename-option aaaabbbb-cccc-dddd-eeee-ffff00004444=Chromium \ --remove-option aaaabbbb-cccc-dddd-eeee-ffff00005555 ``` **Required arguments:** * `` - Field UUID | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--add-option` | `stringArray` | | `[]` | Option label to add (repeat flag for multiple) | | `--display-order` | `int` | | `0` | Position of the field in the ticket form | | `--name` | `string` | | | Field name | | `--placeholder-slug` | `string` | | | Placeholder slug for the field | | `--remove-option` | `stringArray` | | `[]` | Option id to remove (repeat flag for multiple) | | `--rename-option` | `stringArray` | | `[]` | Rename an option as id=Label (repeat flag for multiple) | | `--required` | `bool` | | `false` | Make the field required | | `--required-when-closing` | `bool` | | `false` | Require agents to fill the field when closing a ticket | | `--required-when-submitting-form` | `bool` | | `false` | Require agents to fill the field when submitting the form | | `--state` | `string` | | | Field state: active or inactive | `--rename-option` takes `id=Label`, where the ID is the option's ID from `fields show`. The last option of a dropdown field cannot be removed. ## Delete a field This command deletes a custom ticket field along with the values tickets hold for it. Use this when a field is no longer needed. The field must be `inactive` first, so set it with `fields update --state inactive`. ```bash theme={"system"} neetodesk fields delete aaaabbbb-cccc-dddd-eeee-ffff00003333 ``` **Required arguments:** * `` - Field UUID # 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 | ### fields Manage custom ticket fields | Command | Description | | - | - | | [`neetodesk fields create`](/cli-reference/fields) | Create a custom ticket field | | [`neetodesk fields delete`](/cli-reference/fields) | Delete a custom ticket field | | [`neetodesk fields list`](/cli-reference/fields) | List custom ticket fields | | [`neetodesk fields show`](/cli-reference/fields) | Show a custom ticket field | | [`neetodesk fields update`](/cli-reference/fields) | Update a custom ticket field | ### 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 ``` The named windows are `last_7_days`, `last_30_days`, `last_24_hours`, `this_week`, `last_week`, `this_month`, `last_month`, `this_quarter`, `last_quarter`, `this_year`, `last_year`, `all_time`, and `custom`. Without `--range-type` a report covers the last 30 days. The previous period has the same length as the range, so `this_month` is compared with all of last month. With a signed-in session, a report covers only the tickets you are allowed to see. Every report also returns `start_date`, `end_date`, `previous_start_date`, and `previous_end_date`. 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: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or 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: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or 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: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or 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: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or 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: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or 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" } ] } ] } ``` > [!NOTE] > > `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 | > [!NOTE] > > `--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 with a comma-separated `--status`, where `open` covers both `new` and `open` tickets. Tickets come back newest first unless you pass `--sort` and `--order`. With an API key the list covers the whole workspace; with a signed-in session it covers only the tickets you are allowed to see. ```bash theme={"system"} neetodesk tickets list --status new,open --page-size 25 ``` Other filters narrow the list further and can be combined: ```bash theme={"system"} neetodesk tickets list --status open --unassigned neetodesk tickets list --customer-domain acme.com --status open neetodesk tickets list --priority high,urgent --tag billing --tag vip neetodesk tickets list --query "refund" --range-type last_7_days ``` * `--unassigned` returns tickets with no assignee and cannot be combined with `--assignee-id` or `--assignee-email`. * `--tag` is repeatable and matches tickets carrying any of the given tags. * `--query` matches the subject only. | Flag | Type | Required | Default | Description | | - | - | - | - | - | | `--assignee-email` | `string` | | | Filter by assignee email | | `--assignee-id` | `string` | | | Filter by assignee ID | | `--customer-domain` | `string` | | | Filter by customer email domain (e.g. acme.com) | | `--customer-email` | `string` | | | Filter by customer email | | `--customer-id` | `string` | | | Filter by customer ID | | `--end-date` | `string` | | | End date for custom range (YYYY-MM-DD) | | `--external-id` | `string` | | | Filter by external ID | | `--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 | | `--order` | `string` | | `desc` | Sort direction: asc or desc | | `--page` | `int` | | `0` | Page number | | `--page-size` | `int` | | `0` | Items per page (max 100) | | `--priority` | `string` | | | Filter by priority (comma-separated, e.g. urgent,high) | | `--query` | `string` | | | Text to find in the subject | | `--range-type` | `string` | | | Date range: last\_7\_days, last\_30\_days, last\_24\_hours, this\_week, last\_week, this\_month, last\_month, this\_quarter, last\_quarter, this\_year, last\_year, all\_time, or custom | | `--sort` | `string` | | `created_at` | Sort field: created\_at or updated\_at | | `--start-date` | `string` | | | Start date for custom range (YYYY-MM-DD) | | `--status` | `string` | | | Filter by status (comma-separated, e.g. new,open). "open" includes new tickets | | `--tag` | `stringArray` | | `[]` | Filter by tag, matches any (repeat flag for multiple) | | `--unassigned` | `bool` | | `false` | Only tickets with no assigned agent | ```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 | | `--field` | `stringArray` | | `[]` | Custom ticket field as Name=Value (repeat flag for multiple) | | `--group` | `string` | | | Group name to assign | | `--group-id` | `string` | | | Group ID to assign | | `--name` | `string` | | | Customer name | | `--priority` | `string` | | | Ticket priority (low, medium, high, urgent) | | `--resolution-due-date` | `string` | | | Resolution due date (ISO 8601) | | `--status` | `string` | | | Ticket status | | `--sub-category-one` | `string` | | | Ticket sub category one | | `--sub-category-two` | `string` | | | Ticket sub category two | | `--subject` | `string` | Yes | | Ticket subject | | `--tag` | `stringArray` | | `[]` | Tag to add (repeat flag for multiple) | `--group` takes a group name, `--tag` is repeatable, and `--field` sets a custom ticket field as `Name=Value` and is repeatable. The value is split on the first `=`. ```bash theme={"system"} neetodesk tickets create \ --email customer@example.com \ --subject "Browser crash" \ --description "The page closes on load." \ --group Engineering \ --tag bug --tag vip \ --field Browser=Chrome ``` `--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, sub-categories, resolution due date, custom field values, assignee, group, or tags, and adds files with `--attach`. Use this when you triage or close 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 closed --priority low neetodesk tickets update 1042 --assignee-email oliver.smith@example.com --group Payments neetodesk tickets update 1042 --add-tag billing neetodesk tickets update 1042 --attach ~/Desktop/replacement-part.pdf ``` **Required arguments:** * `` - Ticket number or UUID `--tag` replaces every tag on the ticket. To keep the existing tags, use `--add-tag` and `--remove-tag` (both repeatable) instead; they cannot be combined with `--tag`. Setting the status to `moved` is refused. `--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 | | - | - | - | - | - | | `--add-tag` | `stringArray` | | `[]` | Tag to add, keeping existing tags (repeat flag for multiple) | | `--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 | | `--field` | `stringArray` | | `[]` | Custom ticket field as Name=Value (repeat flag for multiple) | | `--group` | `string` | | | Group name to assign | | `--group-id` | `string` | | | Group ID to assign | | `--priority` | `string` | | | Ticket priority | | `--remove-tag` | `stringArray` | | `[]` | Tag to remove (repeat flag for multiple) | | `--resolution-due-date` | `string` | | | Resolution due date (ISO 8601) | | `--status` | `string` | | | Ticket status, e.g. open, waiting\_on\_customer, closed | | `--sub-category-one` | `string` | | | Ticket sub category one | | `--sub-category-two` | `string` | | | Ticket sub category two | | `--subject` | `string` | | | Ticket subject | | `--tag` | `stringArray` | | `[]` | Replace every tag with these (repeat flag for multiple) | ```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": "closed", "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) | | `--author-email` | `string` | | | Author email (used with an API key; a signed-in session always writes as you) | | `--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. new,open)" } ] } ] } ] ``` ## 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. > [!NOTE] > > 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 > [!TIP] > > 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. > [!TIP] > > 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 > **macOS - shell script** > > ```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`. > **Windows - Command Prompt (CMD)** > > ```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. > [!TIP] > > 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? > **Script your support work** > > Create tickets, post replies, and update statuses from shell scripts and cron jobs. > **Audit from the terminal** > > Pull tickets or agent and group reports, pipe them to `jq`, and create one-off reports without leaving your shell. > **Change things in bulk** > > Page through thousands of tickets in one loop, update them unattended, and switch workspaces with a single flag. > **Built for AI agents** > > 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, custom ticket fields, forms, team members, and reports - and both find tickets by text in their subject. 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. > [!TIP] > > 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). > [!NOTE] > > 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 **--page** `integer` The page of results to retrieve, starting from 1. **--page-size** `integer` 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 } } ``` **pagination.total\_records** `integer` The total number of records across all pages. **pagination.total\_pages** `integer` The total number of pages available. **pagination.current\_page\_number** `integer` The page you are currently on. **pagination.page\_size** `integer` 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.` > **Not authenticated. Run 'neetodesk login' to authenticate.** > > **Problem**: no workspace is signed in. \\ > > **Solution**: run [`neetodesk login --subdomain `](/cli/authentication). > **Multiple subdomains authenticated (acme, beta); specify --subdomain.** > > **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. > **Not authenticated for "foo". Authenticated subdomains: acme, beta.** > > **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. > **Subdomain not found. Please check that you entered the correct subdomain.** > > **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`. > **Authentication session expired. Please try again.** > > **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. > **Could not connect to NeetoDesk. Check your internet connection** > > **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. > **required flag(s) "xxx" not set** > > **Problem**: a required flag was omitted. \\ > > **Solution**: check the command's [reference page](/cli-reference/overview) or run `neetodesk --help` for the required flags. > **API error (422): \** > > **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. > **A ticket you can see in the web app is missing from tickets list** > > **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). > **Claude Code not found (\~/.claude/ does not exist).** > > **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 **1. Open your workspace** Sign in to NeetoDesk in your browser and look at the address bar. **2. Take the part before neetodesk.com** 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** | Move tickets to trash or spam, permanently delete ticket fields, and deactivate team members. Trashed tickets are emptied after 30 days. | 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](https://mintcdn.com/neetodesk-apis/dPZ34efBxF6_a0FO/assets/mcp/oauth-authorize.png?fit=max\&auto=format\&n=dPZ34efBxF6_a0FO\&q=85\&s=abafe2d55ee569c339527b15f57e4da1) 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) > [!TIP] > > 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. > [!WARNING] > > 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 **Claude** 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](https://mintcdn.com/neetodesk-apis/-k9GFWBX8Ilfqswv/assets/mcp/claude-directory-neetodesk.png?fit=max\&auto=format\&n=-k9GFWBX8Ilfqswv\&q=85\&s=1fac4f62839f95374e563468e3e937c7) 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. > **Add it as a custom connector instead** > > 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](https://mintcdn.com/neetodesk-apis/NlTfAMO56t0wyC9a/assets/mcp/claude-add-menu.png?fit=max\&auto=format\&n=NlTfAMO56t0wyC9a\&q=85\&s=6f940eeac20927c64169bf7ab37b2f69) > > 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](https://mintcdn.com/neetodesk-apis/NlTfAMO56t0wyC9a/assets/mcp/claude-add-custom-connector-dialog.png?fit=max\&auto=format\&n=NlTfAMO56t0wyC9a\&q=85\&s=2921fee69c580ba2ccd3b0e1aee9f011) > > 3. Click **Continue** and sign in to NeetoDesk. **ChatGPT web app** 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](https://mintcdn.com/neetodesk-apis/dPZ34efBxF6_a0FO/assets/mcp/chatgpt-developer-mode.png?fit=max\&auto=format\&n=dPZ34efBxF6_a0FO\&q=85\&s=ea83de1ed071513a67606b402ebf972d) 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). **ChatGPT desktop app** 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](https://mintcdn.com/neetodesk-apis/iOLP90HRiHEr0RxR/assets/mcp/chatgpt-desktop-settings-menu.png?fit=max\&auto=format\&n=iOLP90HRiHEr0RxR\&q=85\&s=96584a9ca30aa2b0b2dcba75cd00e2a4) 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](https://mintcdn.com/neetodesk-apis/iOLP90HRiHEr0RxR/assets/mcp/chatgpt-desktop-add-mcp-server.png?fit=max\&auto=format\&n=iOLP90HRiHEr0RxR\&q=85\&s=b95e9a593f7a095f8be51409090adead) 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](https://mintcdn.com/neetodesk-apis/iOLP90HRiHEr0RxR/assets/mcp/chatgpt-desktop-custom-mcp-form.png?fit=max\&auto=format\&n=iOLP90HRiHEr0RxR\&q=85\&s=b4f887c571766c29e94323a56326ab46) 6. Click the **MCPs** tab. Next to **neetodesk**, click **Authenticate**. ![The neetodesk row in the MCPs list, with an Authenticate button](https://mintcdn.com/neetodesk-apis/iOLP90HRiHEr0RxR/assets/mcp/chatgpt-desktop-authenticate.png?fit=max\&auto=format\&n=iOLP90HRiHEr0RxR\&q=85\&s=5c42167bb3e34ab8194464129e5b5f0c) 7. Sign in to NeetoDesk. See [What the sign-in looks like](#what-the-sign-in-looks-like). **Claude Code** 1. Add the server: ```bash theme={"system"} claude mcp add --transport http --scope user 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. **Cursor** 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. **Gemini CLI** 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. **Codex** 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 ``` **VS Code** 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. **Windsurf** 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 **1. Connect your workspace** 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](https://mintcdn.com/neetodesk-apis/dPZ34efBxF6_a0FO/assets/mcp/oauth-connect-workspace.png?fit=max\&auto=format\&n=dPZ34efBxF6_a0FO\&q=85\&s=ab72af0e953a0214942346c1a2c8a38a) **2. Sign in** Sign in to that workspace, if you are not signed in already. **3. Choose what the assistant can do** **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](https://mintcdn.com/neetodesk-apis/dPZ34efBxF6_a0FO/assets/mcp/oauth-authorize.png?fit=max\&auto=format\&n=dPZ34efBxF6_a0FO\&q=85\&s=abafe2d55ee569c339527b15f57e4da1) **4. Authorize** 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). **Claude Code** 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. **Cursor** 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. **Gemini CLI** 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. **Codex** 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. **VS Code** 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. > [!WARNING] > > This file lives in your project. Do not paste the key into the file itself, or it can end up in a commit. **Windsurf** 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**. **Antigravity** 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? > Show me every open ticket from anyone at acme.com. 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 matches ticket subjects and returns the most recent matches, with a snippet of the conversation when the text also appears there. ## 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." > Close ticket #42 and set its priority to low. > Add the tag billing to ticket #42 and keep its other tags. > 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. > [!TIP] > > 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 > **Manage tickets** > > List, search, read, create, and update tickets. > **Reply to customers** > > Post a reply or internal note, and save a draft on a ticket. > **Run reports** > > Ticket volume, agent and group performance, and satisfaction surveys. > **Hand off the busywork** > > 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, custom ticket fields, forms, team members, and reports - and both find tickets by text in their subject. 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. > [!TIP] > > 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. > [!WARNING] > > 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, customer and creation time. Can be narrowed by status (`open` covers both new and open tickets), priority, assigned agent or no agent, customer email or email domain, tags, text in the subject, creation date range, external ID and a custom field value, and sorted by creation or last update. | | [`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. It can also set the status, priority, category, sub-categories, resolution due date, custom field values, assigned agent, group and tags. | | [`UpdateTicket`](/api/tickets/update) | Updates a ticket's subject, description, status, priority, category, sub-categories, resolution due date, custom field values, assigned agent, group or tags, and adds any `attachments` to the ticket's opening message. Tags can be replaced, added to or removed from. Moving a ticket to trash or spam needs the Delete permission. A ticket that has no opening message is refused, and the file goes on a comment instead. | | `search` | Searches tickets by subject, returning up to 25 of the most recent matches, each with a snippet of the conversation when the text also appears there. | | `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. > [!NOTE] > > `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. > [!WARNING] > > `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 | | - | - | | [`ListCustomers`](/api/customers/list) | Lists the workspace's customers, oldest first, with each customer's name, email addresses, phone numbers, language, time zone, notes and company. Can be narrowed to one exact email address. | | [`GetCustomer`](/api/customers/get) | Returns one customer with their name, email addresses, phone numbers, language, time zone, notes and company. | | [`CreateCustomer`](/api/customers/create) | Creates a customer. At least one email address is required, and the first one given becomes the primary. Pass `company_id` to link the customer to a company. | There is no tool for updating or deleting a customer. ## Ticket fields Ticket fields are the workspace's custom fields on tickets. A ticket's values for them are set by field name when creating or updating the ticket. | Tool | What it does | | - | - | | [`ListFields`](/api/fields/list) | Lists the workspace's custom ticket fields in display order, with each field's type, state, required flags and dropdown options. | | [`GetField`](/api/fields/get) | Returns one custom ticket field with its type, state, required flags and dropdown options. | | [`CreateField`](/api/fields/create) | Creates a custom ticket field. Dropdown fields (`single_option` and `multi_option`) need their options. | | [`UpdateField`](/api/fields/update) | Updates a field's name, state, required flags, display order or placeholder, and adds, renames or removes dropdown options. The type cannot be changed. | | [`DeleteField`](/api/fields/delete) | Deletes a custom ticket field and the values tickets hold for it. | Reading fields needs permission to view ticket fields, and creating, updating or deleting them needs permission to manage ticket fields. ## 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, with the number for the period before it and the percentage change. | | [`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. Each value comes with the previous period and the percentage change, and a summary covers all agents. | | [`GetGroupReports`](/api/reports/get-team-performance) | The same metrics per group instead of per agent, with a summary across all groups. | | [`GetSurveyReports`](/api/reports/get-customer-satisfaction) | Reports how customers answered each satisfaction survey over a date range, as a count per answer choice. | Report tools cover the last 30 days unless narrowed with `range_type` (a preset such as `this_month`, or `custom` with `start_date` and `end_date`). Every report also returns the previous period of the same length, so `this_month` is compared with all of last month. # Troubleshooting Source: https://apidocs.neetodesk.com/mcp/troubleshooting What to check when an assistant cannot reach NeetoDesk. > **The assistant does not see any NeetoDesk tools** > > **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). > **ChatGPT does not show Developer mode** > > 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. > **The connection is refused or unauthorized** > > 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. > **The client sends a key when you meant to sign in** > > 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 client asks you to sign in when you meant to use a key** > > 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 OAuth sign-in fails at the workspace step** > > 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). > **An OAuth-connected assistant sees fewer tickets than expected** > > 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). > **A tool is refused because a permission was not granted** > > 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. > **The assistant reaches the wrong workspace** > > 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. > **VS Code never offers the tools** > > 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. > **Windsurf or Antigravity connects but the tools are missing** > > 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. > **Claude shows the connector as unauthorized** > > 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)**. > **A form is missing from the list** > > Only enabled forms are returned. Check that the form is enabled in your workspace settings. > **A draft written by the assistant was not emailed to the customer** > > 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. > **Check that the server is reachable** > > 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 **1. Open your workspace** Sign in to NeetoDesk in your browser and look at the address bar. **2. Take the part before neetodesk.com** 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.