Authentication
Create your API key, understand scopes, and keep the key safe.
The Meteor API authenticates with a secret API key. Every request carries the key in the Authorization header, and the key defines which workspace it belongs to and what it can do (its scopes).
Key format
Every key starts with a prefix that tells you its type:
met_live_…— production key. It performs real actions and spends Energy.met_test_…— test mode key: runs cost nothing, which is what you want while you build. External side effects (sending WhatsApp messages, running integrations) are blocked. It is not an isolated sandbox: unrestricted scopes still read and write the real workspace.
The key is a server-side secret. Never ship it to a browser or a mobile app, and never commit it. If it leaks, revoke it from the dashboard and create a new one.
Create a key
In your Meteor dashboard: Settings → Developers → Create API key. You pick the scopes it needs and the environment (live or test). The key is shown once — store it the moment you create it.
You can create and manage keys before the workspace has a plan. To use them, the workspace needs a current monthly Meteor plan; a trial or a sponsored plan also enables operation. Approved Tech Partners may receive a special USD 0 fixed monthly plan enabled only by Meteor, with Energy billed at the premium rate.
Use the key
Keep it in an environment variable, never hardcoded:
export MET_API_KEY=met_live_your_key_here
With the SDK:
import Met from '@meteor.ia/sdk';
const met = new Met(process.env.MET_API_KEY, { workspaceId: 7 });import os
from meteor_ia import Met
met = Met(os.environ["MET_API_KEY"], workspace_id=7)With curl, the key goes as a Bearer token:
curl https://api.met.meteor.com.co/api/v1/workspaces/7/runs \
-H "Authorization: Bearer $MET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"Hello"}'
Know which identity you are calling with
A key does not tell you whose it is just by looking at it. To ask the server, use GET /me: it returns the identity the API recognises for the key you are calling with — who owns it, which workspace it operates, with what scopes and in which environment.
curl https://api.met.meteor.com.co/api/v1/me \
-H "Authorization: Bearer $MET_API_KEY"const me = await met.me.retrieve();
console.log(me.workspace?.id, me.api_key.scopes, me.livemode);me = met.me.retrieve()
print(me["workspace"]["id"], me["api_key"]["scopes"], me["livemode"])The response as it arrives on the wire. Every successful API response travels inside an envelope, and the resource lives in data:
{
"success": true,
"data": {
"object": "identity",
"livemode": true,
"api_key": {
"id": "key_01HXYZ",
"object": "api_key",
"owner_type": "workspace",
"env": "live",
"scopes": ["items:read", "runs:execute", "runs:read"],
"rate_limit_rpm": 120,
"monthly_quota": null
},
"workspace": { "object": "workspace", "id": 7, "slug": "acme" },
"partner": null
}
}
The official SDKs hand you the contents of data directly, which is why yo in the examples above is the inner object. If you call with curl or your own client, read .data. A client generated from the contract already knows: the OpenAPI declares the envelope. See Errors and idempotency.
What each field is for:
| Field | What you get from it |
|---|---|
api_key.id | Identifies the row in the dashboard and in your request logs. It is not the secret and it authenticates nothing: you can paste it into a support ticket safely. |
api_key.owner_type | workspace if the key belongs to a customer, partner if it belongs to a Tech Partner. |
api_key.scopes | What the key can do, according to the server. When a call returns 403 missing_scope, this is the list to check it against. |
livemode / api_key.env | Whether what you do has real effects. Read it here rather than from the key prefix: it is the same criterion the server applies. |
workspace.id | The workspace this request operates. With a workspace key it is always its own; with a partner key it is the attributed customer you are operating. |
partner.id | The partner that owns the key, or null. |
rate_limit_rpm · monthly_quota | The ceilings that actually apply to you: the stricter of the key's and your plan's. null means no cap. These are the numbers behind a 429. |
The endpoint never returns the key secret, nor its prefix, its last characters or its length. If you lost the key you cannot recover it here: create a new one in the dashboard.
GET /me currently requires the runs:read scope. If your key does not carry it, the call returns 403 missing_scope — the key is valid, it just cannot reach this endpoint.
From the terminal
met whoami prints the same thing, and --save-workspace stores the workspace the server resolved in your local config, so you no longer pass --workspace on every command:
met whoami
met whoami --save-workspace
met whoami --json
If your local config points at one workspace and the key operates another, met whoami says so: that mismatch is the biggest time sink there is, because everything answers 200 and the data simply is not the data you expected.
Scopes
A key can only do what it has a scope for. Scopes follow the pattern domain:action, where :write covers create, update and delete (there is no separate :update). Ask for the ones your integration needs and nothing else.
These tables come from the API scope catalog and the OpenAPI contract. The Endpoints column says how many REST operations ask for that scope today.
A — under Endpoints means no REST operation asks for that scope today. It stays valid: a key that already carries it keeps working.
Watch out for conversions:read: no REST endpoints, but webhook events behind them. To subscribe an endpoint to those events, you need the scope.
rag:read, rag:write and sites:read are no longer offered when issuing a new key or registering an OAuth app. Keys that already carry them are unaffected; they simply unlock no calls.
Running Mets
| Scope | Allows | Endpoints |
|---|---|---|
agents:read | View Mets | 6 |
agents:write | Create and configure Mets | 10 |
runs:execute | Run Mets — spends Energy | 2 |
runs:read | Read runs and history | 3 |
skills:read | View skills | 9 |
skills:manage | Create, edit and remove skills | 12 |
functions:read | View AI Functions (code and Met tools) | 5 |
functions:write | Create and edit AI Functions | 6 |
rag:read | Query indexed collections | — |
rag:write | Index content so it can be queried later | — |
flows:write | Create, edit, duplicate and archive flows | 5 |
automations:read | View triggers | 14 |
automations:write | Create, edit and delete triggers | 7 |
automations:execute | Fire automations | 4 |
Data
| Scope | Allows | Endpoints |
|---|---|---|
collections:read | Read collections | 13 |
collections:write | Create, edit and delete collections | 22 |
items:read | Read items | 9 |
items:write | Create, edit and delete items | 27 |
contacts:read | Read CRM contacts | 12 |
contacts:write | Create, edit and delete contacts | 17 |
tasks:read | Read agentic tasks (the steps a Met runs) | 7 |
tasks:write | Create, edit and delete tasks | 16 |
variables:read | Read workspace variables | 5 |
variables:write | Create, edit and delete variables | 6 |
files:read | Read files and the media library | 2 |
files:write | Upload and delete files | 7 |
Conversations and channels
| Scope | Allows | Endpoints |
|---|---|---|
conversations:read | Read WhatsApp and CRM conversations | 9 |
conversations:write | Create and edit chat quick replies | 3 |
handoff:manage | Autopilot, assignment and operator message | 4 |
channels:read | Channel status and broadcasts | 5 |
channels:send | Send over WhatsApp and broadcasts | 13 |
channels:manage | Create and delete WhatsApp templates | 2 |
Integrations and platform
| Scope | Allows | Endpoints |
|---|---|---|
integrations:read | View MCP integrations | 3 |
integrations:manage | Enable integrations and store their credentials | 3 |
integrations:execute | Run integration tools | 4 |
mcp:use | Open a session on the MCP server | 2 |
webhooks:manage | Inbound webhooks and outbound event subscriptions | 20 |
events:read | Workspace events: live and from the log | 6 |
reminders:read | View reminders | 1 |
reminders:write | Create, edit and delete reminders | 3 |
billing:read | Plan and usage (there is no billing:write) | 11 |
conversions:read | Conversion status and settings, plus the conversion.sent and conversion.discarded events | — |
conversions:write | Report a conversion to Meta (CAPI) | 2 |
sites:read | View websites | — |
snapshots:read | Browse the template catalog and view your own | 2 |
snapshots:install | Install a template into the workspace | 2 |
Partner keys only
These scopes cannot be issued on a workspace key: the server re-checks on every request and answers 403. See the Partners API.
| Scope | Allows | Endpoints |
|---|---|---|
snapshots:publish | Publish a template to the marketplace | 1 |
workspaces:provision | Create client accounts | 3 |
workspaces:recharge | Top up energy on an account you provisioned | 1 |
partner:billing:manage | The partner's payment method, used to pay for the accounts it manages | 2 |
partner:clients:read | Attributed clients | 1 |
partner:leads:read | View leads | 4 |
partner:leads:write | Create, edit, move and delete leads | 7 |
partner:projects:read | View implementation projects | 5 |
partner:projects:write | Create and edit projects, tasks, milestones and phase progress | 10 |
partner:commissions:read | Commissions (always read-only) | 1 |
partner:payouts:read | Payouts (always read-only) | 2 |
partner:support:read | Your own tickets and your clients' | 3 |
partner:support:write | Open tickets, reply and change status | 3 |
What test mode does and does not do
A met_test_ key cannot exercise scopes with real external effects: integrations:execute, channels:send, conversions:write, channels:manage, workspaces:provision, workspaces:recharge, partner:billing:manage and snapshots:install answer 403 with test_mode_restricted. The restriction holds over REST and over the MCP server alike: a tool whose scope is restricted does not even appear in the catalog your agent sees.
That, plus cost, is everything test mode separates from production:
With a met_test_ key | |
|---|---|
| Run Energy | Not debited: the run is recorded at zero cost. |
| How many runs | There is a daily cap on test runs per workspace. Once you hit it, 429 with test_run_daily_cap. Your plan can raise that cap, or set it to zero. |
| External effects | The scopes above answer 403 test_mode_restricted. |
livemode | false on the run resource and on the events that run produces. |
| Where the data lives | In the same database and the same tables as production. There is no copy, no mirror, no separate environment. |
| Contacts, items, collections, tasks, files | Real. If the key carries items:write, your tests create items your team sees in the dashboard. |
| Undo | None. Nothing is rolled back when the key expires or is revoked. |
Only two things carry the livemode mark and can therefore be told apart afterwards: runs and events. Everything else you write with a test key is indistinguishable from what your production wrote, so there is also no way to ask for "delete my test data": those records are deleted one by one, like any other.
The one exception cleans itself up: a run with no conversation_id runs in an ephemeral conversation, and those are purged after a few days — whether they came from a test key or a live one.
To test without touching your data, from least to most work:
- Issue the test key with read scopes only (plus
runs:executeif you are going to run Mets). It is a one-line decision at creation time and it closes the whole problem: what has no write scope does not write. - Use a separate workspace for development and issue the test key there. It is the closest thing to a separate environment that exists today, and it is what you can delete wholesale when you no longer need it.
- Look at the shape of the responses first with the portal's demo key: it asks for no sign-up, runs against a fictional business, and is read-only. It is on the home page, under *Not ready to sign up?*.
If a key tries something outside its scopes, the API answers 403 with the code missing_scope, and the param field carries the missing scope. Do not retry — ask the key owner to widen the scopes.
Revoke
A revoked key stops working in under 60 seconds. Revoke and rotate keys at the first sign of a leak, and use a different key per environment (one for test, one for production).
Apps that operate across workspaces
If you are building an app that other workspaces connect, do not ask them for an API key. Register an OAuth app in your dashboard: Settings → Developers → OAuth apps. The user sees the requested scopes and authorizes (or revokes) your app.
Follow the OAuth apps guide: it uses Authorization Code with PKCE S256, exact callbacks, and rotating refresh tokens.