Tested against Help Scout Mailbox API v2.0 as of 2026-05-27.
This document lists every Help Scout API endpoint used by hscli, including request/response details, the CLI command that calls it, and known quirks.
Base URL: https://api.helpscout.net
Exchange credentials for an access token.
| Detail | Value |
|---|---|
| Content-Type | application/x-www-form-urlencoded |
| Response | 200 OK -- JSON |
| CLI commands | hscli auth login, hscli auth setup, hscli auth refresh |
| Docs | OAuth 2.0 Authentication |
Request body (form-urlencoded):
grant_type=authorization_code|client_credentials|refresh_token
client_id=<app_id>
client_secret=<app_secret>
code=<auth_code> # authorization_code only
redirect_uri=<redirect_uri> # authorization_code only
refresh_token=<refresh_token> # refresh_token only
Response:
{
"access_token": "...",
"refresh_token": "...",
"token_type": "Bearer",
"expires_in": 172800
}Quirks:
- The token endpoint requires
application/x-www-form-urlencoded, notapplication/json. Sending JSON will return a 400 error. This differs from many modern OAuth implementations. expires_inis 172800 seconds (48 hours) for both grant types.- The
refresh_tokenfield is only present in Authorization Code responses, not Client Credentials responses.
List conversations with optional filters.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 200 OK -- JSON (paginated) |
| CLI command | hscli conv list |
| Docs | List Conversations |
Query parameters:
| Param | Type | Notes |
|---|---|---|
status |
string | active, pending, closed, spam, all |
mailbox |
integer | Mailbox ID |
tag |
string | Filter by tag name |
assigned_to |
integer | User ID |
query |
string | Search query |
modifiedSince |
string | ISO 8601 datetime |
page |
integer | Page number (1-indexed) |
Response:
{
"_embedded": {
"conversations": [...]
},
"page": {
"size": 50,
"totalElements": 123,
"totalPages": 3,
"number": 1
}
}Quirks:
- Pagination is 1-indexed (first page is
page=1, notpage=0). modifiedSincerejects timestamps with milliseconds. The CLI strips.NNNzto produceYYYY-MM-DDTHH:mm:ssZ.
Get a single conversation by ID, including thread details.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 200 OK -- JSON |
| CLI command | hscli conv get <id> |
| Docs | Get Conversation |
Response: Full conversation object with _embedded threads.
Create a new conversation.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 201 Created -- JSON |
| CLI command | hscli conv create |
| Docs | Create Conversation |
Request body:
{
"subject": "Help needed",
"type": "email",
"mailboxId": 123,
"customer": { "email": "user@example.com" },
"threads": [{ "type": "customer", "text": "Message body" }],
"status": "active",
"tags": ["billing"],
"assignTo": 456
}Notes:
typecan beemail,chat, orphone.tagsis an array of tag name strings.assignTois optional; set to a user ID to assign on creation.
Add a customer-facing reply to a conversation.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 201 Created |
| CLI command | hscli conv reply <id> |
| Docs | Create Thread |
Request body:
{
"type": "reply",
"text": "Thanks for reaching out",
"draft": false,
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"]
}Notes:
- Set
draft: trueto save without sending. ccandbccare optional arrays of email strings.
Add an internal note (not visible to the customer).
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 201 Created |
| CLI command | hscli conv note <id> |
| Docs | Create Thread |
Request body:
{
"type": "note",
"text": "Internal note content"
}Update conversation fields using JSON Patch (RFC 6902).
| Detail | Value |
|---|---|
| Content-Type | application/json-patch+json |
| Response | 204 No Content |
| CLI commands | hscli conv status, hscli conv assign, hscli conv move |
| Docs | Update Conversation |
Request body (status change):
[{ "op": "replace", "path": "/status", "value": "closed" }]Request body (assign):
[{ "op": "replace", "path": "/assignTo", "value": 456 }]Request body (move to another mailbox):
[{ "op": "replace", "path": "/mailboxId", "value": 789 }]Notes:
- Uses
application/json-patch+jsoncontent type, not regularapplication/json. - Multiple operations can be sent in a single request.
Replace all tags on a conversation.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 204 No Content |
| CLI command | hscli conv tag <id> |
| Docs | Update Tags |
Request body:
{
"tags": ["billing", "urgent"]
}Notes:
- This is a full replacement -- it sets the complete tag list. The CLI handles add/remove by first fetching current tags (via GET), merging the changes, then sending the full list.
Delete a conversation.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 204 No Content |
| CLI command | hscli conv delete <id> |
| Docs | Delete Conversation |
Notes:
- The CLI prompts for confirmation before deleting. Use
--yesto skip the prompt.
List all mailboxes.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 200 OK -- JSON (paginated) |
| CLI command | hscli mailbox list |
| Docs | List Mailboxes |
Response:
{
"_embedded": {
"mailboxes": [...]
},
"page": { ... }
}Get a single mailbox by ID.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 200 OK -- JSON |
| CLI command | hscli mailbox get <id> |
| Docs | Get Mailbox |
Get the currently authenticated user.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 200 OK -- JSON |
| CLI commands | hscli user me, hscli auth status, hscli conv assign <id> --user me |
| Docs | Get Current User |
Response:
{
"id": 123,
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"role": "owner",
"timezone": "America/New_York"
}Create a new customer.
| Detail | Value |
|---|---|
| Content-Type | application/json |
| Response | 201 Created -- JSON |
| CLI command | hscli customer create |
| Docs | Create Customer |
Request body:
{
"firstName": "Jane",
"lastName": "Doe",
"emails": [{ "value": "jane@example.com" }],
"phones": [{ "value": "555-1234" }],
"organization": "Acme",
"jobTitle": "Support Lead"
}Notes:
emailsandphonesare arrays of objects with avaluefield, not plain strings.
Update a customer using JSON Patch.
| Detail | Value |
|---|---|
| Content-Type | application/json-patch+json |
| Response | 204 No Content |
| CLI command | hscli customer update <id> |
| Docs | Update Customer |
Request body:
[
{ "op": "replace", "path": "/firstName", "value": "Janet" },
{
"op": "replace",
"path": "/emails",
"value": [{ "value": "new@example.com" }]
}
]Notes:
- Uses
application/json-patch+jsoncontent type. - Supported paths:
/firstName,/lastName,/organization,/jobTitle,/emails,/phones.
All list endpoints use page-based pagination:
- Pages are 1-indexed (
page=1is the first page). - Response includes a
pageobject withtotalElements,totalPages,size, andnumber. - Resources are nested under
_embedded.<resourceKey>(e.g._embedded.conversations).
Non-2xx responses return:
{
"message": "Description of the error",
"status": 422
}- Rate-limited requests return
429 Too Many Requests. - The
x-ratelimit-retry-afterheader indicates how many seconds to wait. - The CLI automatically retries with the indicated delay (up to 3 attempts). Use
--no-retryto disable.
All API requests (except /v2/oauth2/token) require:
Authorization: Bearer <access_token>
Operations not available in the public Mailbox API v2:
- Delete thread/note: Only via Help Scout web UI (internal
/api/v1/with session auth). The public API returns400onDELETE /v2/conversations/:id/threads/:threadId— onlyPATCHis supported on threads. - OAuth apps are account-scoped: No public/shared app model. Each user must create their own OAuth app in Help Scout.
- PATCH format: Conversation patches use a single JSON Patch object
{op, path, value}, not an array[{...}]. Customer patches use the array format. - Reply requires customer:
POST /v2/conversations/:id/replyrequires acustomer: {id}field in the body. The CLI fetches the conversation first to resolve this automatically.