AI Agents
An AI Agent is a worker for your business. It runs on your sites and data, follows playbooks you give it (Skills), can run code, call APIs, and send email — and it asks before anything big, shows you every step, and can never overspend. You build, run, and manage the whole thing just by chatting in plain English — everything below is what happens under the hood.
You do all of this by chatting
You never have to wire any of this up by hand. Describe what you want and it creates the agent and everything it needs — skills, scripts, triggers, functions, even the webhook — and edits it later the same way. There are two places to chat:
The built-in Build chat on the AI Agents page writes and edits the agent, its skills, and its scripts for you — just tell it what the agent should do.
Connect Claude, ChatGPT, or any MCP client over MCP and manage your whole team of agents by chatting there — build, run, and check on them without leaving your AI.
Delegate to an agent from any chat — type @
In every chat on the platform — a site, a Base, a Flow, Automations, or your team page — type @ to pull in one of your agents. Pick from the list (keyboard or mouse, several at once), send, and the agent runs the task; its result comes back into the chat.
- It gets a focused task, not your whole conversation — plus the context of what you're looking at.
- The result flows back in, and the chat carries on from it. Ask a research agent for the facts, and the chat builds the table or article from what it found — automatically, no extra step.
- The agent still follows its own permissions, approvals, and limits — a delegation can never do anything the agent itself couldn't.
Run an agent on your own AI provider
Agents are not limited to the platform models. Add a key under Your AI (left rail) — OpenAI, Anthropic, Google, Azure OpenAI, Amazon Bedrock, Mistral, Groq and 20 more — and that provider's models appear in the agent's model list, one entry per key (“GPT-5.4 · OpenAI (Work)”). Runs on your own key bill no Serenities credits.
- Keep several named keys per provider and pick which one an agent uses; rename a key or replace its secret without touching the agent.
- The same keys work in every chat and in functions (
ctx.ai.generate). - See Your AI for the full provider list and setup.
1. Create an agent
Go to Automations → AI Agents. The fastest way is to just tell the chat what you want, in plain English — it builds the whole agent for you, including any skill and script:
"Build an agent called Contact Concierge. When a contact form is
submitted, classify the enquiry, look up our opening hours, and email
the person a reply. Give it a webhook I can point my form at."You (or the AI) set a few things, all editable later:
- Identity & instructions — who the agent is and how it works.
- Abilities — the exact tools it may use (read data, write rows, send email, fetch the web, run functions…). Nothing outside this list exists for the agent.
- Sites & Bases it may touch — data access is granted, never assumed.
- Approval mode — Ask first (pauses before anything external or destructive) or Full autonomy.
- Limits — spend per run, runs per day, actions per run, and a hard time limit.
2. How agents run (triggers)
One runtime, six ways to start a run — all under the agent's Triggers tab (the same rows a function has; Automations → All triggers lists them across the account):
- Manually — type a task in the Run box and watch it work.
- On a schedule — repeating (say it in plain English, e.g. “every weekday at 9am”) or a one-off (“in 2 weeks”). A run missed during downtime is caught up once on restart.
- Webhook — a secret URL. POST JSON to it from anywhere (a contact form, another app) and the body becomes the agent's task.
- Event (when something happens) Beta — a row added or changed in a base (narrowed to which base, which table, only when a field changes, only rows where…), a file, a flow or agent run, a site event, a payment, a connector, or your own event. Write what to ask the agent with
{{properties.row.Customer}}-style placeholders, or let it read the event as-is. - Incoming mail (email address) — give the agent an address; every email that arrives becomes a task.
- Hand-off — one agent passes its full result to another to continue the work.
3. Skills & the SKILL.md format
A Skill is a reusable playbook an agent follows for a kind of task. Serenities uses the open Agent Skills (SKILL.md) standard — the same format as Claude and ChatGPT skills — so a skill from any public library drops in unchanged. A skill is a folder:
my-skill/
├── SKILL.md # required: YAML frontmatter + instructions
├── references/ # docs the agent loads only when it needs them
├── scripts/ # Python or Node the agent runs (stdin → stdout)
└── assets/ # templates/files used in the outputThe SKILL.md front-matter carries the standard fields:
---
name: contact-triage
description: Classify a contact enquiry and answer it from the FAQ.
Use whenever a contact form is submitted.
license: Apache-2.0
allowed-tools: [skills_run_script, web_fetch, email_send]
---
# Contact triage
1. Run scripts/classify.py to categorise the message.
2. Read references/faq.md for the facts.
3. Reply to the person by name using only those facts.Three ways to add a skill to an agent:
- Ask the chat — “give this agent a skill that…” and it writes the SKILL.md and scripts for you.
- Upload a
.zip/.skillbundle (or a singleSKILL.md) on the Skills tab. - The agent writes its own — when Learning is on, an agent saves a playbook once it finds a procedure that works.
Every skill lives in a shared Skills library (the Skills tab), so once you have one you can reuse it across as many agents as you like — tick it on for another agent there, or just ask the chat to. Edit it in one place and every agent that uses it stays in sync.
Progressive disclosure keeps it cheap: only each skill's name + description sit in the agent's context at all times; the full playbook loads when the skill is triggered, and a reference or asset loads only when the agent actually opens it.
4. When does an agent use a skill?
This is the important part. Every skill's name and description are always in the agent's system prompt. When a task comes in, the agent matches the task against each description and, if one fits, reads the full skill first and follows it. It is the same mechanism Claude uses — the description is the trigger.
So triggering is only as good as your description. Write it to say both what the skill does and when to use it:
- Third person, specific. “Extracts text and tables from PDFs. Use when the user uploads a PDF or mentions forms.”
- Include the trigger words a real request would contain (“refund”, “invoice”, “shipping”).
- Avoid vague descriptions like “helps with documents” — the agent can't tell when to reach for it.
If an agent isn't using a skill you expected, tighten its description — that is almost always the fix.
5. Scripts — Python & Node
A skill's scripts/ hold real code — Python or Node — for the deterministic parts a program does better than prose (parsing, maths, formatting, date logic). The agent calls a script, which reads its input as JSON on stdin and returns its result on stdout:
# scripts/classify.py
import sys, json
d = json.load(sys.stdin)
msg = d["message"].lower()
urgent = any(w in msg for w in ["urgent", "refund", "damaged"])
print(json.dumps({"category": "support", "urgent": urgent}))Every script runs in a locked-down sandbox — this is the whole point:
- No network, no filesystem beyond the skill itself — it can't read your secrets or reach your database.
- Capped CPU, memory, processes, and time — a runaway or malicious script can't affect anything else.
Because scripts have no network, fetching external data is the agent's job: the agent uses web_fetch (below) to get the data, then passes it into the script as input. Clean separation — the fetch is guarded, the script stays airtight.
6. Functions & external APIs
There are two ways an agent reaches the outside world, for two different needs:
Fetch a public URL or open API. HTTPS only; internal/private addresses are blocked. Great for public data (prices, holidays, public records). It cannot send auth headers by design.
A backend/account function is your own code (with ctx.fetch, your data, email) that the agent invokes and gets a result from. This is how an agent calls an authenticated API — the key stays inside the function (next section).
To let an agent run a function, grant it the invokeFunction ability. Functions are also how you give an agent bespoke logic that doesn't belong in a skill script (it needs your database, or a secret, or to send email as part of the work).
There's a third route outward: connected apps. Anything you've connected under Connections (Gmail, Slack, and the like) can be handed to an agent — and you choose exactly which of that app's tools it may use, switching each one on or off. Using a connected app reaches outside your account, so it pauses for approval too.
7. Secrets & API keys — never exposed to the agent
You can give an agent the power to call an API that needs a key without the key ever reaching the agent or the AI model — the same principle Claude uses (the secret is substituted into the request server-side; the model never holds it). The recipe:
- Store the key as a Secret (Automations → Secrets). It's encrypted at rest and is write-only — nothing, not even an agent, can ever read the value back.
- Write a function that uses it with a
{{PLACEHOLDER}}. The real value is filled in inside the sandbox, at the moment of the call:
// function: chargeCustomer
const res = await ctx.fetch("https://api.stripe.com/v1/charges", {
method: "POST",
headers: { Authorization: "Bearer {{STRIPE_KEY}}" }, // filled server-side
body: JSON.stringify({ amount: params.amount }),
});
return { charged: res.status === 200 }; // only THIS returns to the agent- Grant the agent
invokeFunctionand let it callchargeCustomer.
The key is decrypted only inside the throwaway sandbox that runs the function, injected straight into the outbound request, and only the function's return value ({ charged: true }) comes back to the agent. The API key is never in the agent's prompt, its context, or its transcript.
8. Sending email
Grant the email ability and an agent can send replies, reminders, and reports. It sends through the platform's email service from your verified sender, and it counts against your plan's monthly email quota. You can also send from one of your own connected mailboxes so the reply comes from your address. Sending is an external action, so it pauses for approval unless the agent is on full autonomy.
9. Approvals & safety
Agents are safe by default. In Ask first mode, the run pauses and waits for you before anything that reaches outside or can't be undone:
- External — sending email, calling a function, or using a connected app.
- Publish — pushing a site live or unpublishing it.
- Payments — anything touching money.
- Destructive — deleting a project, table, rows, or a function.
On top of that: every run has a spend cap, a daily run limit, an action limit, and a hard time limit — reach any and the run stops cleanly. You see every step it took, the exact input and output of each tool call, and can hit Stop at any moment. Scripts are sandboxed (§5) and secrets stay server-side (§7).
10. Example: a contact form answered by an agent
Here is everything above working together — a real pattern you can build today.
- Build the agent (“Contact Concierge”) and give it a skill,
contact-triage, with an FAQ reference, a Python classifier, and a Node script — plus a webhook, the web ability, and the email ability. - Put a form on your site that POSTs the fields to the agent's webhook URL:
await fetch("https://app.serenitiesai.com/api/agents/webhook/<token>", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email, subject, message }),
});On each submission the agent runs its skill end to end:
- Reads the skill, then runs
classify.py(Python) → category, urgency, priority. - Uses
web_fetchto pull live UK bank-holiday data, then runsschedule.js(Node) to work out the next working day for a reply. - Reads the FAQ and policy references for the facts.
- Writes a tailored answer and
email_sends it to the person — grounded only in your FAQ.
The visitor gets a real, on-brand answer by email in a couple of minutes; you get a full transcript of every step in the dashboard.
For developers
Agents are fully available over MCP — Claude Code, Codex, or any MCP client can build, run, and manage them with the team_* tools. Backend functions can hand work to agents in one line: ctx.agents.enqueue('Agent name or id', task, data). Skills are the open SKILL.md standard, so anything you author here is portable.