Tutorial: Build A Support Agent
This tutorial creates a small but working agent repo. It uses the public JSONPlaceholder API as a fake support system, so the first deploy does not need secrets, OAuth, or customer data.
By the end, the agent should be able to:
- read a fake ticket from
https://jsonplaceholder.typicode.com/todos/{id} - read the related fake customer from
/users/{id} - follow a triage skill before recommending action
- write structured investigation notes to a store
- run evals before promotion
- show useful sessions, tool calls, logs, and deploy history in Amodal
1. Create The Repo
Create a new GitHub repo with this tree:
amodal.json
amodal/
connections/support-demo/
spec.json
policy.json
surface.md
knowledge/
support-policy.md
skills/
triage/SKILL.md
stores/
investigation-notes.json
evals/
ticket-triage.md
account-owner.mdConnect the repo to a new agent in Amodal, or use Build With A Coding Agent to create and connect it with the Platform API.
2. Add amodal.json
{
"name": "support-demo",
"version": "0.1.0",
"description": "Tutorial agent for support-ticket triage",
"session_types": {
"default": {
"prompt": "Help support operators triage demo tickets. Check available data before recommending action. Separate facts from assumptions.",
"skills": ["triage"],
"connections": ["support-demo"],
"stores": {
"investigation-notes": "rw"
}
}
}
}Model selection is platform state, not an amodal.json field. If you do not choose a model, the platform default is Gemini 2.5 Flash.
3. Add A Public Connection
amodal/connections/support-demo/spec.json:
{
"baseUrl": "https://jsonplaceholder.typicode.com",
"format": "rest",
"testPath": "/todos/1"
}This API is public, so there is no auth block and no secret to configure.
amodal/connections/support-demo/surface.md:
# Support Demo API
This is a fake support system backed by JSONPlaceholder.
Use these read-only endpoints:
- `GET /todos/{id}`: load a fake support ticket.
- `id` is the ticket ID.
- `userId` is the related customer ID.
- `title` is the issue summary.
- `completed` means the issue is resolved.
- `GET /users/{id}`: load the fake customer account.
- `id` is the customer ID from the ticket's `userId`.
- `name`, `email`, and `company.name` are safe to summarize.
When asked about a ticket, first load `/todos/{id}`. If the ticket includes a `userId`, load `/users/{userId}` before recommending action.amodal/connections/support-demo/policy.json:
{
"endpoints": {
"GET /todos/{id}": {
"returns": ["Ticket"]
},
"GET /users/{id}": {
"returns": ["Customer"]
}
},
"fieldRestrictions": [
{
"entity": "Customer",
"field": "email",
"policy": "retrieve_but_redact",
"sensitivity": "pii_name",
"reason": "Email can help identify the account but should not be repeated unless necessary."
}
]
}4. Add Knowledge
amodal/knowledge/support-policy.md:
# Support Policy
- State which ticket and customer records were checked.
- Treat `completed: true` as resolved and `completed: false` as still open.
- Separate confirmed facts from assumptions.
- Do not expose credentials, payment tokens, or private internal notes.
- When data is missing, ask for the missing ticket or customer identifier.
- End with one concise next step for the support operator.Knowledge files are loaded into the agent context at session start. Keep stable policy here; keep step-by-step reasoning in skills.
5. Add A Skill
amodal/skills/triage/SKILL.md:
# Skill: Triage
Use this workflow for support-ticket questions.
Trigger: When the user asks about a ticket, customer issue, support case, or next operator action.
## Behavior
1. Identify the ticket ID from the user message.
2. Load `GET /todos/{id}` from the `support-demo` connection.
3. If the ticket has a `userId`, load `GET /users/{userId}`.
4. Summarize confirmed facts:
- ticket ID
- issue title
- open/resolved state
- customer name or company when available
5. Recommend one next support action.
6. If useful, write a short record to `investigation-notes`.
## Constraints
- Do not invent ticket fields that are not returned by the API.
- Do not call write endpoints; this demo connection is read-only.
- If the API call fails, explain that the ticket could not be loaded.6. Add A Store
amodal/stores/investigation-notes.json:
{
"name": "investigation-notes",
"entity": {
"name": "InvestigationNote",
"key": "{ticket_id}",
"schema": {
"ticket_id": { "type": "string" },
"summary": { "type": "string" },
"next_action": { "type": "string" },
"confidence": {
"type": "enum",
"values": ["low", "medium", "high"]
}
}
},
"history": { "versions": 3 },
"trace": true
}This gives the agent a structured place to persist a concise note per ticket. The generated store tools will be named store__investigation_notes__get, store__investigation_notes__set, store__investigation_notes__query, and so on.
7. Add Evals
evals/ticket-triage.md:
# Eval: Ticket Triage
Tests whether the agent checks ticket data before recommending action.
## Setup
Context: Ticket 1 exists in the demo support API.
## Query
"Triage ticket 1. What should the operator do next?"
## Assertions
- Should load or reference ticket 1 before answering
- Should identify whether the ticket is open or resolved
- Should provide one concise next action
- Should NOT invent private customer dataevals/account-owner.md:
# Eval: Account Owner
Tests whether the agent follows the ticket's userId to the customer record.
## Setup
Context: Ticket 1 has a related userId.
## Query
"Who owns ticket 1 and what is the issue?"
## Assertions
- Should check the ticket record
- Should use the ticket's userId to check the related customer
- Should summarize the issue title
- Should avoid unnecessary personal details8. Deploy
Commit and push the repo. In Amodal:
- Open the agent.
- Open Source and confirm the files are visible.
- Deploy the current commit.
- Open Deploys and wait for the build to complete.
- Promote the deploy if it is not already active.
Before chatting, check Connections. The support-demo connection should show the files from amodal/connections/support-demo/ and the health check should be able to reach /todos/1.
9. Smoke Test
Ask these in the deployed chat:
Triage ticket 1. What should the support operator do next?
Who owns ticket 1, and what company are they associated with?
Triage ticket 2 and save a short investigation note.A good answer for ticket 1 should say that it checked ticket 1, identify the issue title, recognize that completed: false means the ticket is still open, load the related user 1, and give one next support action.
If the answer is generic and does not mention tool data, inspect:
- Sessions: confirm there is a
requesttool call. - Logs: check for connection, auth, or provider errors.
- Prompt: confirm the
triageskill andsupport-policyknowledge loaded. - Guardrails: confirm the source-backed connection policy lists only read endpoints.
- Deploys: confirm production points at the deploy that contains these files.
10. Run Evals
Open Evals and run the two eval files. The first run should become your baseline. After that, change one thing at a time, redeploy, and rerun evals.
Good first iterations:
- Tighten
amodal/skills/triage/SKILL.mdso the agent always loads the related customer. - Add a knowledge rule that unresolved tickets should end with a next operator action.
- Add another eval for ticket
2. - Ask the agent to save an investigation note and verify the store tool call appears in the session.
11. What To Build Next
Once this tutorial works, replace the demo API with your real system:
- Change
amodal/connections/support-demo/spec.jsonto your API base URL and auth. - Update
surface.mdwith your real endpoints and field meanings. - Tighten
policy.jsonbefore enabling write endpoints. - Add secrets in Amodal instead of committing credentials.
- Add evals for your highest-risk support behaviors.
The mechanics stay the same: repo files define behavior, Amodal deploys a materialized runtime, sessions show what happened, and evals catch regressions before promotion.