Build your product on Amy
The shape of an integration that holds up in production — and a map to every guide you will need.
Amy is meant to be the intelligence inside your product: memory, tools and agents, behind your own sign-in and your own interface. Your users talk to your product; your product talks to Amy. This page is the pattern. Each section links to the guide that has the detail; for a first call, start with the Quickstart.
The pattern at a glance
- The Amy key lives on your back end and nowhere else.
- Each product is its own Amy organisation.
- Each of your users is an
endUserRefon every run. - Your back end streams Amy's answer on to your UI.
- Your systems are tools, over MCP, with a short-lived grant per run.
- Risky tool calls pause for approval; your UI asks the user.
- Webhooks tell you when work that took a while is done.
- Files go in and out through your back end too.
1. The key stays on your server
An Amy API key can run agents, spend your allowance and read the conversation of any end user it names. Treat it like a database password. Put it in your server's secret store, call Amy from your back end, and never ship it to a browser, a mobile app or a desktop client — anything in a client can be read by the people who use it. Keep a different key per environment so a leak can be revoked alone. See Authentication and Set up your organisation.
Because the key is not tied to a person, your sign-in decides who a user is. Amy
trusts the endUserRef you send. Derive it from your own authenticated session, never from
something the browser can set freely.
2. One organisation per product
Keys, provider keys, agents, tool servers, webhooks, memory and usage all belong to an organisation. Give each product its own, so they cannot read each other's conversations or share a bill. A staging copy of the same product should have its own key, and ideally its own organisation.
3. Identify every end user
Send endUserRef on every run. It is an opaque id of your own — a user id or a random
token, never an email, a name or a phone number: Amy keeps it in audit entries for 12 months, and
erasure does not remove it (see Data and privacy).
It does three jobs:
- Conversations. A chat belongs to the
conversationIdyou generated and theendUserRefthat started it. Another user asking for it gets a 404. List a user's chats withGET /v1/conversations. - Memory. What an agent remembers about a user is kept per end user. Without an
endUserRefall such runs share one unnamed memory — a leak between your users. - Erasure. When a user asks to be forgotten, an Admin erases by this id.
More in Data and privacy and Runs.
4. Stream through your back end
For a chat UI, start a streaming run from your back end and relay the frames to the browser. Your
back end authenticates the user, picks the endUserRef, attaches the tools and grants,
and passes Amy's server-sent events through. The browser only ever sees your domain.
import express from 'express';
import { Readable } from 'node:stream';
const app = express();
app.use(express.json());
// Your own sign-in decides who this is. Amy never sees your users' passwords or sessions.
app.post('/api/chat', async (req, res) => {
const user = await requireSignedInUser(req); // your code; returns { id } or ends the request
const upstream = await fetch('https://api.askamy.co.za/v1/agents/amy/runs', {
method: 'POST',
headers: {
'X-Api-Key': process.env.AMY_API_KEY!, // lives on this server only
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: String(req.body.message ?? ''),
endUserRef: user.id, // an opaque id, never an email or a name
conversationId: req.body.conversationId, // a UUID your app generated for this chat
threadConversationHistory: true, // the agent sees this chat's earlier turns
tools: ['recall', 'remember', 'mcp_list_tickets'], // the complete list: the agent's memory plus your own tool (check toolWarnings)
mcpAuth: [{ serverSlug: 'crm', token: `Bearer ${await mintCrmToken(user)}` }]
})
});
if (!upstream.ok || !upstream.body) {
// Before the stream opens, Amy answers with a normal status and a ProblemDetails body.
return res.status(upstream.status === 429 ? 429 : 502).json({ error: 'assistant_unavailable' });
}
res.status(200).set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' });
Readable.fromWeb(upstream.body as never).pipe(res); // relay the frames to your UI
});- A failure before the stream opens is a normal HTTP status; one after is an
errorevent inside a200. Handle both. See Streaming and Errors. - You decide which frames your UI shows. Relaying everything is fine for a developer tool; for a consumer product you may want to drop tool-call frames and show only the text.
- Plan for
429: honourRetry-After. See Limits.
5. Your own tools
The strongest reason to build on Amy is that the agent can act on your data. Run an MCP
server in front of your product, register it once in the Console, and name its tool handles in tools on every run. tools is the complete list for the run: to add your
MCP tools and keep the agent's own (memory's recall and remember, web
search, and so on), name those too. Their names are on the agent's page in the Console, under Tool
approvals. Mint a short-lived mcpAuth token for the user the run is for, so your
server enforces that user's permissions, not a superuser's. While you integrate, log the toolWarnings of every answer: a run that goes ahead without some of your tools lists
them there, and it should stay empty in production. Details and failure behaviour are in Tools and MCP. Agents are in Agents.
6. Approvals
Tools can be set to need a person's go-ahead, and every inline MCP tool always does. When a run
reaches one it pauses and emits needs_approval; your UI shows the user what the agent
wants to do, and your back end sends their decision to the resume endpoint. If nobody is watching,
the amy.run.approval_requested webhook tells you a run is waiting. See Approvals.
The default amy agent asks before it saves a fact (remember) and every
time it forgets one (forget), so an unattended run that tries pauses at awaiting_approval. For unattended runs, create your own agent and set those approval
levels (Agents).
7. Work that takes a while
Do not hold a connection open for a long job. Submit the run, store the runId, and
let a webhook tell you it finished; then fetch the result with GET /v1/runs/{runId}.
Submit suits runs that need no per-run token, inline server or files. For a long run that does,
start a streamed run, keep the runId from run_started, and hang up: the
run carries on and the webhook tells you when it ends.
curl -X POST https://api.askamy.co.za/v1/agents/amy/runs/submit \
-H "X-Api-Key: $AMY_API_KEY" -H "Content-Type: application/json" \
-d '{
"message": "Prepare the monthly account summary for account 4182.",
"endUserRef": "user_8841"
}'
# 202 Accepted
# { "runId": "0197a000-0000-7000-8000-000000000001", "status": "pending" }
# Amy then calls your webhook with amy.run.completed (or .failed / .approval_requested).Verify every delivery's signature and de-duplicate on eventId. See Webhooks. You can always poll the run instead.
8. Files
Upload a user's file from your back end with POST /v1/files, attach the returned id
in attachmentFileIds, and read what a run produced from its file routes. Delete uploads
you no longer need. See Files.
Production checklist
| Before launch | Guide |
|---|---|
| Organisation exists, provider key and default model are set, a first call returns 200. | Set up your organisation |
| One key per environment, kept in a secret store; rotation rehearsed. | Authentication |
| The agent you want exists, with instructions and approval levels you chose. | Agents |
Every run sends endUserRef from your authenticated session, and a conversationId per chat. | Runs |
| Your UI handles both error shapes, and cancel. | Streaming, Errors |
Tools are registered, named in tools, and a test question only your server
can answer is answered. | Tools and MCP |
| Approvals are surfaced to a person, with a plan for ones nobody answers. | Approvals |
| Webhook endpoint verifies signatures, answers within 10 seconds and de-duplicates. | Webhooks |
You back off on 429, and know your plan's allowances. | Limits, Usage |
Privacy policy chosen; you know how to erase a user; no personal data in endUserRef. | Data and privacy |
| Your client tolerates new fields, events and codes. | Versioning and support |