Skip to content

Scaffold an app with support

Support is normally the thing you add in month four, after the first angry email arrives at an address nobody is watching. It does not have to be. If your agent is connected to Helmdesk while you are still scaffolding the app, the whole support layer is one prompt at the start of the project.

This page walks through what that prompt does, step by step, so you know what you are agreeing to before you run it.

What this needs

A key that reaches all projects and holds projects:manage (to create the project) and api_keys:manage(to mint the app's own key). The second is outside every preset — add it deliberately. See Safety & approvals.

The prompt

Said in your editor, with the new project open:

Set up Helmdesk for this app. It is called Ledgerly — invoicing for freelancers.
Create the project, mint a key limited to it with the scopes this app needs,
write the key and project into .env.local, and show me the widget snippet.

What follows is four tool calls and two file edits. None of it emails anyone.

Step 1 — the project

The agent calls create_project with the name and a description. One project per app: it gets its own ticket queue, help centre, email templates, error log, sandbox, and public support pages.

The response carries the line you want in your environment file, so the agent does not have to construct it:

create_project response (trimmed)
{
  "id": "…",
  "name": "Ledgerly",
  "slug": "ledgerly",
  "env": { "HELMDESK_PROJECT": "ledgerly" },
  "nextStep": "…"
}

Agents do not invent product names

create_project is documented to be called only when you have named the app. If you have not, the agent should ask rather than guess — a project is a real thing on your bill, and a stray one called my-app is annoying to clean up.

Step 2 — a key that belongs to the app

This is the step people get wrong by hand. The key your agent holds is a broad, all-projects key. The key inside your app should be neither: it belongs to one project and holds only the scopes that app actually uses.

The agent calls create_api_key scoped to the new project. A minted key can never exceed the caller — it only reaches projects the caller reaches and only holds scopes the caller holds — so there is no privilege escalation to worry about.

Typical scopes for an app that emails, logs, and files tickets
emails:send
logs:write
tickets:write
customers:write

The raw key is returned once

The secret appears in that one response and is never retrievable again. It should go straight into your environment file and nowhere else — not into a commit, not echoed back into the chat, not into a README. If it is lost, revoke it with revoke_api_key and mint another.

A live key may mint a sandbox key for your dev and staging config, which is the right thing to put in a .env.development: nothing sent from it can reach a real inbox. A sandbox key can never mint a live one.

Step 3 — the environment file

Two lines, written by the agent into .env.local:

.env.local
HELMDESK_API_KEY=sk_live_…
HELMDESK_PROJECT=ledgerly

That second line does more than it looks like. Any future agent session opened in this repo reads it and knows which project this codebase belongs to, so you never have to say "the Ledgerly one" again. On a multi-project account that is the difference between an agent that acts confidently and one that stops to ask every time.

Step 4 — the widget

get_project returns the install snippet with your real slug already in it, plus the public support and help-centre URLs. Drop it into your layout and customers have somewhere to write to:

<script src="https://helmdesk.dev/widget.js" data-project="ledgerly" async></script>

It needs no API key — the project slug is enough, and the public endpoint is CORS-enabled. See Embeddable Widget for identified users and theming.

Then keep going

Everything above takes about a minute. The useful part is that the agent is still connected afterwards, so the rest of the support layer is more sentences rather than more setup:

  • A welcome email."Write a welcome template for Ledgerly and preview it with my name in it" — import_email_template then preview_email, no sending.
  • A starter help centre."Draft articles for the five questions a new invoicing app gets, and leave them as drafts" — create_article defaults to draft, so nothing goes public until you say so.
  • Error logs. Point your app at write_log with the key you just minted, and errors group themselves into issues the Log Watch agent can alert you on. See Logs.
  • A webhook into your own stack."Register a webhook for new tickets pointing at my Slack relay, then test it" — create_webhook then test_webhook, which fires a synthetic event and tells you exactly what your endpoint answered.

Doing it for an app that already exists

Same flow, with one addition: you probably already have customers. If your app knows its own user ids, have the agent pre-load the directory so the first ticket from someone arrives with their history attached rather than as a stranger.

Read the users table in this repo's schema, then create Helmdesk customers for
the twenty most recent signups, setting externalId to our own user id and
tagging each one with their plan.

create_customer takes an externalId, so afterwards get_customerresolves your app's user id straight to their support history. Tags are namespaced (plan:gold), and set_customer_tags can sync one namespace at a time without disturbing the others — which is what makes it safe to run on a schedule. See Customers.

Try the whole thing in sandbox first

Everything on this page except create_project works in the sandbox plane. If you want to watch the flow before pointing it at a real project, set HELMDESK_ENVIRONMENT=sandbox, run it, then reset_sandbox to wipe what you made.