Skip to content

Tools, SDK and errors

Forty-four operations, three ways to call them, and the refusals they share.

Support is headless. There are three ways to reach it and they are all the same surface: the support_* MCP tools, the REST routes under /support, and the support namespace of @lessly/sdk-app. Every operation exists in all three, with the same inputs, the same outputs and the same refusals.

The tools

Forty-four operations. Twenty act on the support model itself — the dictionaries a product configures, then the threads, messages and files that fill it — eight configure and inspect the outbound webhooks that carry events away from it, eleven belong to surveys and the responses collected against them, and five are the delivery rules that say where a survey is shown.

Every REST path below is documented field by field on the Support API reference, and every tool has its own page under MCP tools.

The support model

ToolRESTWhat it does
support_status_listGET /support/statusesThe status dictionary, seeding the defaults on first read
support_status_createPOST /support/statusesAdd a status
support_status_updatePATCH /support/statuses/{id}Rename, recolour, reorder, re-categorise
support_status_archivePOST /support/statuses/{id}/archiveRetire a status, moving its threads to a target
support_agent_createPOST /support/agentsCreate an agent profile
support_agent_listGET /support/agentsThe product’s agents
support_agent_getGET /support/agents/{id}One agent, deactivated ones included
support_agent_updatePATCH /support/agents/{id}Edit a profile, including deactivating it
support_label_createPOST /support/labelsAdd a label
support_label_listGET /support/labelsThe label dictionary
support_thread_createPOST /support/threadsOpen a thread on behalf of an end user
support_thread_listGET /support/threadsThe queue, with every filter
support_thread_getGET /support/threads/{id}One thread, status joined in
support_thread_updatePATCH /support/threads/{id}Title, priority, labels, metadata
support_thread_status_setPOST /support/threads/{id}/statusMove a thread to a status
support_thread_assignPOST /support/threads/{id}/assignmentSet or clear the assignee
support_message_createPOST /support/messagesPost a message into a thread
support_message_listGET /support/messagesOne conversation, or the product-wide catch-up
support_attachment_createPOST /support/attachmentsReserve a file and get an upload URL
support_attachment_finalizePOST /support/attachments/finalizeConfirm the upload landed

Webhooks

ToolRESTWhat it does
support_webhook_createPOST /support/webhooksRegister an endpoint and mint its signing secret
support_webhook_listGET /support/webhooksThe product’s endpoints, never with a secret
support_webhook_getGET /support/webhooks/{id}One endpoint
support_webhook_updatePATCH /support/webhooks/{id}URL, events, description, and active against disabled
support_webhook_deleteDELETE /support/webhooks/{id}Remove an endpoint and its delivery history
support_webhook_rotate_secretPOST /support/webhooks/{id}/rotate-secretReplace the signing secret and return the new one once
support_webhook_deliveries_listGET /support/webhook-deliveriesThe delivery log, filtered by endpoint, state, event or time
support_webhook_redeliverPOST /support/webhook-deliveries/redeliverSend a past delivery again, with its original key

What each event carries and how to verify its signature is Receive Support events.

Surveys and responses

ToolRESTWhat it does
support_survey_createPOST /support/surveysDraft a survey with its question set
support_survey_listGET /support/surveysThe product’s surveys
support_survey_getGET /support/surveys/{id}One survey, any status
support_survey_updatePATCH /support/surveys/{id}Edit a draft, including its questions
support_survey_publishPOST /support/surveys/{id}/publishOpen it for answers, freezing the structure
support_survey_unpublishPOST /support/surveys/{id}/unpublishReturn it to draft and stop accepting responses
support_survey_archivePOST /support/surveys/{id}/archiveRetire a survey for good
support_survey_statsGET /support/surveys/{id}/statsServer-side aggregates, NPS included
support_response_listGET /support/responsesThe responses collected, with every filter
support_response_getGET /support/responses/{id}One response
support_response_escalatePOST /support/responses/{id}/escalateTurn one response into a feedback thread

Submitting a response is deliberately not a tool: a response is written by a person filling in a form, so POST /support/surveys/{surveyId}/responses is an HTTP route and nothing else. The lifecycle, the question types and the skip rules are Surveys and delivery rules.

Delivery rules

ToolRESTWhat it does
support_rule_createPOST /support/rulesWrite a rule: where a survey is shown, on which devices, when and how often
support_rule_getGET /support/rules/{id}One rule, with its whole configuration
support_rule_listGET /support/rulesThe product’s rules, newest first, filtered by survey, state, page URL or device
support_rule_updatePATCH /support/rules/{id}Edit a rule, including switching it off. The survey is not editable
support_rule_deleteDELETE /support/rules/{id}Remove a rule for good — a rule is deleted, not archived

Nothing in a rule is executed here. What each field means, and which half of the decision belongs to your own runtime, is Delivery rules.

The MCP tools are the management plane: this is how an agent, the Workspace app and an operator all drive Support, and it is the surface the platform’s tool catalogue advertises.

The generated SDK namespace

There is no hand-written Support SDK package to install. Support publishes its operation catalogue, and @lessly/sdk-app compiles a support namespace from it — one method per operation above, typed from the same input and output schemas the tools declare. Update the SDK and new operations appear; nothing about Support is maintained twice.

The public perimeter is a narrow subset

Everything above is behind the platform login. The MCP tools, the authenticated REST routes they map to and the generated SDK namespace all require a platform identity, and none of them answers a product public key.

Beside them Support publishes a small keyed perimeter for a client’s own backend: twelve routes on https://public.lessly.com/{productId}/support, reached with a product public key rather than a platform login. They open a thread on behalf of an end user, read the queue and one thread, post and read messages, read the status and label dictionaries, read the agent profiles a reply is signed with, read a published survey and submit a response to it, and ask which delivery rules apply to a page. That is the whole list — see Public HTTP API.

The subset is deliberate. Assignment, status changes, creating and editing agent profiles, attachments and the webhook registry are not published on the public perimeter and answer 404, and no specialist bound to a Lessly identity writes through it — only an unbound agent profile, a bot or the client’s own backend, may author a message there. And the key is a backend credential: an end user’s browser must never talk to Support directly, because everything here is product-scoped support data and an end user is not a caller Support can recognise.

So a thread opened on behalf of an end user is opened by your backend. Your product’s support screen talks to your backend, and your backend talks to Support.

Who is calling

Every operation is authenticated, and the identity always belongs to your side: a specialist signed into the Workspace app, your own backend, or an agent. It answers one question — is this call legitimate, and which product does it belong to — and it never touches the content of a thread. The end users your threads are about are not part of it at all.

The product is sealed to the caller’s identity. It is not a parameter on a single tool, and it is not read from the request body: there is no way to ask Support for another product’s data by naming it, which is why none of the tools above take a product argument.

Errors

CodeMeans
400The input is invalid, or the operation is refused: an archived status as a destination, a deactivated agent as an assignee, an attachment that is not claimable, cursor and offset together
401The call carries no usable identity
404The thing named does not exist — or belongs to another product
409A uniqueness or history conflict: a duplicate status, label or Lessly binding, or re-categorising a status that threads already sit on

Validation errors name the fields that failed. Refusals say what is wrong and, where there is one, what to do instead — the refusal to re-categorise a status in use tells you to create a status in the target category and move the threads.

An error carries a code and a message; a successful tool call returns exactly the value its output schema describes, so what you get back over HTTP is what you get back over MCP.

Not found, never forbidden

Anything belonging to another product is reported as not found, and the message never says that it exists somewhere else or who owns it. A thread, a status, an agent, a label, an attachment — all the same answer.

This is deliberate. “Forbidden” tells the caller the id is real, which is itself information about another customer’s data. Between “there is no such thread” and “there is a thread and it is not yours”, Support always says the first, so probing for ids learns nothing.

The rule applies at every depth, not only to the object you name: a message authoring against another product’s agent, a thread created with another product’s label, an attachment claimed from another product’s thread — each is a plain not-found.

Where to go next

Was this page helpful?
Esc

Start typing to search the docs.

navigateselect