Skip to main content
In this tutorial you extend the quickstart agent into a support agent for an online shop. It looks up orders and opens tickets from data the platform stores for it, receives order events through a webhook, posts a daily ticket count, answers from uploaded policy documents, and runs on your website. Each step adds one primitive and tests it on its own; only the last two steps touch production. Finished, it answers like this in production:
Output
Plan about 30 minutes. You need the project from the quickstart, signed in, with agent version 1 promoted. Verified against lua-cli 3.33.0.

What you’ll build

  • A support skill with two tools: lookup_order reads an order from Data, and create_ticket writes one.
  • An order-status webhook that stores each order event so lookup_order has something to find.
  • A persona in object form, with separate instructions for text channels and voice calls.
  • Knowledge Search over documents you upload in the admin dashboard.
  • A daily-summary job that counts open tickets every morning.
  • A released agent version, embedded on a web page with the chat widget.
1

Code: Add a support skill with two tools

Both tools use Data, the agent-scoped JSON store; collections (orders, tickets) are created on first write. Data.get takes the collection, a filter, a page number, and a page size, and returns { data, pagination }; each entry keeps your fields under data. lookup_order returns { found: false } instead of throwing, because the model reads the return value and the skill’s context tells it what to say when the order is missing.
src/skills/tools/LookupOrderTool.ts
create_ticket writes an entry and returns its ID so the model can read it back to the end user. The zod describe() strings are part of what the model sees, so write them as instructions for filling the field.
src/skills/tools/CreateTicketTool.ts
The skill’s context tells the model when to call each tool and what to do with a miss.
src/skills/support.skill.ts
Register the skill beside the weather skill.
src/index.ts
Run create_ticket once. lua test executes your code on your machine, but Data calls reach the platform, so the ticket is stored on the agent.
Output
Run lookup_order for the same order. You should see found: false: nothing has written to orders yet, which the next step fixes.
Output
2

Code: Store order events with a webhook

A webhook is an HTTP endpoint your code handles outside any conversation; no model runs. This one validates the body with zod (a request that fails bodySchema is rejected before execute runs), then creates or updates the order’s entry so the same order number never gets two entries.
src/webhooks/OrderStatusWebhook.ts
Register it on the agent.
src/index.ts
lua test webhook takes the request as { "query", "headers", "body" } and runs execute with it.
Output
Run lookup_order again. You should see the status the webhook stored.
Output
In production, sign the calls: set secret on the webhook and send x-lua-signature; see Handle a webhook.
3

Code: Write the persona for text and voice

The persona is the text the model reads before every conversation. In object form, base is rendered on every channel, then text is appended on text channels and voice on voice calls. The skill context stays where it is: the persona says who the agent is, the context says when to call which tool.
src/index.ts
Talk to the agent in the sandbox. lua chat -e sandbox compiles your code and uploads it as sandbox versions the platform runs; the first run after a new skill registers it on the server and may answer without it (Skipping skill support - no skillId found in lua.skill.yaml), so run the command twice. You should see the model call lookup_order and report the shipped status.
Output
4

Dashboard: Add knowledge

Knowledge is the set of documents the agent retrieves from while the Knowledge Search feature (rag) is on; it lives on the server, not in your code. Open the agent in the admin dashboard (lua admin opens it), select the Knowledge tab, and upload a document such as your returns policy. You should see the document in the tab’s list once its processing finishes. From the terminal, lua resources list prints the same list.rag retrieves passages from your uploads and puts them in front of the model when a question matches. lua init asks for every capability feature, so the agent normally starts with rag on; confirm from the terminal, where the first entry should read Active.
Output
If it reads Inactive, turn it on with lua features enable --feature-name rag. Ask the sandbox agent a question the document answers, such as “What is your returns policy?”; it answers from the upload.
5

Code: Schedule a daily summary job

A job runs on a schedule with no conversation attached, so there is no end user, no thread, and no model unless your code calls one. schedule is cron (with an optional IANA timezone), interval in seconds, or once at a date; timeout is in seconds. This one counts open tickets at 09:00 London time, stores the total, and returns it.
src/jobs/DailySummaryJob.ts
Register it; this is the agent’s final shape.
src/index.ts
lua test job runs execute now instead of waiting for the schedule. You should see the ticket from step 1 counted.
Output
6

Terminal: Release the agent

lua push all compiles, bumps the patch version of every primitive, uploads each one plus the persona and a source backup, and records the new versions in lua.skill.yaml. The skills, webhook, and job change nothing for end users until you promote; the persona is served from the next message. --force skips the confirmation, and --ci fails instead of prompting.
Output
Snapshot the pushed state as agent version 2, then promote it. A version records which pushed version of each primitive, which persona version, and which model belong together; promoting switches every primitive in one step, and promoting 1 again would roll the code back. lua version list shows which version is active.
Output
From this moment every end user gets version 2, and the job fires at its next 09:00. Roll back with lua version promote 1; see Release an agent to production.
7

Code: Put the agent on your website

The web widget is the channel end users reach from a browser: one script tag, no build step, and it talks to the same promoted version as lua chat -e production. Set environment: "production" and replace agent_abc123 with the agentId in lua.skill.yaml.
index.html
Open the page. You should see a chat button in the bottom-right corner, and “Where is order A-1001?” gets the same answer as lua chat -e production. Colors, position, and welcome text are covered in the web widget quickstart.
Ask production about the order. The answer comes from lookup_order reading the entry your webhook wrote, under the new persona.
Output

Remove the test records

Data is shared by the sandbox and production, so the ticket, order, and summary you created stay on the agent. There is no CLI command for entries; delete them from code with Data.delete(collection, entryId). Add this tool to the support skill, run it once with lua test, then remove it before your next push.
src/skills/tools/ClearTestDataTool.ts

What you learned

  • A skill’s context plus each tool’s description is how the model decides when to call your code, and lua test runs a tool without a model (About skills and tools).
  • Data is the agent-scoped store tools, webhooks, and jobs share, which is how a webhook can feed a tool (Data reference).
  • A webhook handles HTTP outside any conversation, and lua test webhook replays one request locally (About webhooks).
  • The persona’s object form gives text channels and voice calls different instructions on top of a shared base, and a pushed persona is served at once (About persona).
  • Knowledge lives on the server and needs the rag feature; uploads and features are managed from the admin dashboard or lua features (About knowledge and features).
  • A job runs on a cron, interval, or one-time schedule; lua test job runs it immediately (About jobs).
  • lua push uploads, lua version create snapshots, and lua version promote makes a snapshot live or rolls back (About releases and versions).

Next steps

Handle a webhook

Sign requests with secret, verify x-lua-signature, and make handlers idempotent.

About execution contexts

What User, Data, and env resolve to in a tool, a webhook, and a job.

LuaAgent reference

Every field you can register on the agent, with types and defaults.