How to build an AI agent
What an agent is, how it differs from a flow-based chatbot, and the five steps to having one answering on WhatsApp with access to your business data.
An AI agent is a program that takes an instruction in natural language, decides which tools to use to carry it out, runs them and answers. What separates it from a normal integration with a language model is the word *decides*: nobody wrote the sequence of steps up front.
In Meteor an agent is called a Met, and it is three things: a prompt that defines what it does, a set of tools it can use, and access to the business's data. The language model is the fourth component and the most interchangeable one — it is not what determines whether the agent is any good.
Agent or flow: the decision to make first
It is the most expensive confusion, because you pay for it in production and not in development.
| agent | flow | |
|---|---|---|
| decides the sequence | the model, on every execution | you, when you build it |
| good for | what cannot be foreseen | what cannot be improvised |
| example | answering a question nobody anticipated | charging, dispatching, notifying |
| fails by | answering something reasonable but wrong | freezing in the face of the unexpected |
The right answer is almost never one of the two: the agent handles the conversation and calls a flow when something has to be executed that admits no improvisation. An agent that charges a customer while deciding the amount is an incident waiting its turn; a flow that tries to handle a complaint is a form in disguise.
In Meteor both exist and they call each other — a Met can trigger a flow as one of its tools.
The five steps
1. Get an API key
From the panel, under Settings → Developers. Start with a test key (met_test_): it runs for free, with a daily cap, and the scopes that produce external side effects stay restricted — there is no way to message a real customer by accident. Careful, though: that blocks sending, not writing. If you grant it :write scopes, a test key writes to your real workspace, so issue it read-only while you explore.
npm install @meteor.ia/sdk
Full detail in Authentication.
2. Define what the agent does
The prompt is the agent's definition, not decoration. What decides quality is not its length but its precision about what it must not do and when to hand over to a human.
A Met is created from the panel or over the API, and that is where you assign its capabilities. See Mets and their tools.
3. Give it access to the business data
An agent without data improvises. In Meteor data lives in collections — workspace tables the agent reads and writes without you programming an endpoint for each one:
import Met from '@meteor.ia/sdk';
const met = new Met(process.env.MET_API_KEY!, { workspaceId: 7 });
const cols = await met.collections.list();
await met.items.create(cols[0].id, { data: { titulo: 'New enquiry' } });
If the data lives in another system, you connect it as MCP integrations and the agent uses them as tools.
4. Run it
const run = await met.runs.create({ input: "Summarize today's enquiries" });
console.log(run.output);
With stream: true the answer arrives token by token over SSE, which is what you want in a chat interface. See Run Mets and Streaming.
5. Connect it to a channel
An agent that only answers over the API is not serving anyone. By connecting WhatsApp, Messenger or Instagram to the workspace, the Met replies in the contact's thread:
await met.contacts.sendMessage(42, 'The visit is booked');
There is a limit here worth knowing before you write code: outside the 24-hour window since the person's last message, WhatsApp only allows writing with templates approved by Meta. It is not a Meteor limit and it cannot be worked around. See CRM and contacts.
What almost always gets forgotten
An agent needs to be able to give up. The case that decides whether people write to it again is not the one it resolves well, but the one it cannot resolve: if there is no way out to a human, the conversation dies there. In Meteor that is autopilot — it pauses for a few minutes and resumes on its own, instead of switching off until somebody remembers. See Workspace operations.
Runs are billed, so you have to be able to look at them. GET /billing/executions gives one row per run with its cost. When the total doesn't add up, the answer is almost always an agent calling itself in a loop.
Everything an agent did stays auditable. Every run has its trace, and the request_id of the HTTP response ties it to what you see in the panel. See Live events.
Using Meteor from your own agent
If you already have an agent — in Claude, in Cursor, or one of your own — you don't need the API: Meteor exposes a remote MCP server with a catalog of met_* tools. Your agent runs Mets, queries the CRM, triggers tasks and reads data as native tools:
https://api.met.meteor.com.co/api/v1/mcp
The catalog is filtered by your API key's scopes: a tool whose scope you don't have simply does not appear. See MCP server.
Frequently asked questions
Do I need to train a model?
No. An agent is defined by prompt, tools and data; the model is an interchangeable component. Training makes sense when the problem is that the model doesn't *know* something about the domain, and almost always the real problem is that it *can't do* something — and that is fixed with a tool, not with training.
How long does it take to get one working?
A Met answering with access to a collection is a few hours. What takes time is the rest: defining when it gives up, what it must not say, and what happens when the tool it needs is down.
Can I test without touching real data?
Not with the test key alone. A met_test_ key gives you three things: runs debit no Energy (with a daily cap per workspace), they come out marked livemode: false, and the scopes with external effects —sending over WhatsApp, running integrations, provisioning accounts— answer 403. That prevents the worst accident, which is writing to a real customer.
What it does not do is isolate you from your data: it runs against the same database and the same tables. If the key carries items:write, your tests create real items, and nothing rolls them back when the key expires.
To test without touching your data, the simplest move is to issue the test key with read scopes only, plus runs:execute: what has no write scope does not write. If you need to write, do it in a separate workspace created for development, which you can delete wholesale. Full detail in Authentication.
Which languages have an SDK?
TypeScript (@meteor.ia/sdk) and Python (meteor-ia) have official packages. Check the published version and its examples before choosing your client. There is also a CLI (met) and the REST API with its published OpenAPI.
What if my case isn't conversational?
A good part of what people build is not a chat: it is a procedure that runs on its own. Those are tasks — they fire over the API, on a schedule or on an event, and their steps can run Mets.