Safety & approvals
Handing an agent your support desk means handing it the ability to email your customers. That is the whole point, and it is also the thing worth being deliberate about. This page is the honest version: what can reach a real person, what cannot be undone, and the three mechanisms that keep both in bounds.
The short version
Of 73 tools, 10 reach a customer and 10 delete something permanently. The other 53are safe to let an agent call freely. You bound them with the key you issue, the environment you point it at, and your client's own approval prompts — in that order of strength.
The tools that reach a real person
These 10 send mail to someone who is not you. There is no unsend.
| Tool | Who gets mail, and when |
|---|---|
| reply_to_ticket | The customer, immediately, with a link back to the thread. |
| update_ticket | The customer — but only when you set status to resolved, which sends a satisfaction survey. Other field changes send nothing. |
| bulk_update_tickets | Every customer whose ticket you resolve in the batch. One call, up to 100 surveys. |
| send_email | The recipient, from one of your templates. |
| send_email_batch | Up to 100 recipients in a single call. |
| send_custom_email | The recipient, from a subject and body written on the spot. |
| resend_email | The original recipient of a logged send, again. |
| reply_to_feedback | The customer, on a feedback conversation rather than a ticket. |
| request_feedback | The customer, asking them to rate you — unless channel is link. |
| approve_agent_item | The customer, when the item you approve is a drafted auto-reply. Approving is what sends it. |
Two of those surprise people, so they are worth saying twice. Resolving a ticket emails the customer, and approving a drafted reply is what sends it. Neither reads like "send an email" in a prompt, and both do.
The tools that delete
10 tools remove data with no trash to recover it from: delete_ticket, delete_email_template, delete_article, delete_log_issue, delete_log_events, delete_feedback_request, delete_customer, delete_webhook, revoke_api_key, reset_sandbox. Note that reset_sandbox is on that list and is genuinely destructive, but only ever to sandbox data — it cannot touch live, whatever key you hold.
1. The key is the real boundary
Client approval prompts are a convenience. The API key is enforcement: a scope the key does not hold is a 403 no matter what the agent decides to try. Issue the smallest key that does the job.
A read-only key is a genuinely useful thing to start with. The agent can answer every question about your support desk and change none of it:
tickets:read
customers:read
articles:read
feedback:read
logs:read
emails:read
projects:readAdd write scopes one at a time as you decide you want them. The scope that matters most is emails:send — without it, six of the ten outbound tools stop existing for that key. Leaving out tickets:write removes the other four.
Two scopes to add deliberately
api_keys:manage lets the agent mint and revoke keys. It is outside every preset for that reason. Add it when you want an agent scaffolding new apps, and understand that a key holding it can create other keys — bounded by its own projects and scopes, but still.
logs:delete is deliberately separate from logs:write, so the key your app ships logs with cannot erase your error history.
Keys are also bounded by project. A key issued for one project cannot see another, so an agent working in one repo cannot reach into a different app's customers. See Authentication.
2. Rehearse in sandbox
Every project has a second data plane. In sandbox, mail is rendered and recorded in Email Logs and then not delivered; tickets never notify the customer or your team; webhooks fire only to sandbox endpoints. It is the right place to run a flow you have not run before.
You do not need a second key. Set a default for the whole session:
HELMDESK_ENVIRONMENT=sandboxOr let the agent choose per call, which is how you rehearse one thing without moving everything:
Send the welcome email to jane@example.com in sandbox, and show me how it rendered.The downgrade runs one way only. A live key may ask for sandbox on a single call; a sk_sandbox_ key asking for live is a 403 environment.forbidden, by id or otherwise. So a sandbox key is a hard guarantee that nothing you do reaches a customer — which makes it the right thing to hand an agent you are still learning to trust. Full detail in Sandbox.
3. Your client already knows which is which
Every tool ships machine-readable annotations, so a client can tell a read from a write from a send before it runs anything. A tool named list_tickets is marked read-only; reply_to_ticket is marked as reaching outside the system and as not undoable. Clients that support per-tool approval use those hints to decide what to wave through and what to stop on.
The descriptions carry the same information in words, because the model reads those. Every outbound tool's description states who receives the mail, that it cannot be unsent, and what happens instead in sandbox. That is enforced by the server's own test suite rather than left to whoever wrote the string.
What the built-in agents do about this
Helmdesk's own background agents follow the same principle from the other direction: they mostly propose rather than act. The Auto-Responder drafts a reply and leaves it in the review queue; nothing is sent until a human — or an agent you have explicitly asked to do it — approves. Auto-Close acts but is revertible.
Which means a good default is to let your coding agent read the queue freely and make approving a separate, deliberate sentence. See Automations for what each one does.
A sensible progression
- Start with a read-only key. Ask questions for a few days.
- Add write scopes but set
HELMDESK_ENVIRONMENT=sandbox. Run the flows you care about end to end where nothing can escape. - Move to live with
emails:sendstill withheld. The agent can triage, tag, note, and draft, but not send. - Add sending last, once you have watched it draft for a while.
Most people who get burned skip straight to step four on day one.