On this page
Guides / Authentication

Authentication

Create your API key, understand scopes, and keep the key safe.

Updated View .md

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:

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:

FieldWhat you get from it
api_key.idIdentifies 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_typeworkspace if the key belongs to a customer, partner if it belongs to a Tech Partner.
api_key.scopesWhat 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.envWhether what you do has real effects. Read it here rather than from the key prefix: it is the same criterion the server applies.
workspace.idThe 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.idThe partner that owns the key, or null.
rate_limit_rpm · monthly_quotaThe 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 asks for the identity:read scope, which every key has without ticking it: any valid key can ask who it is, even with no other scope. Up to version 3.27.0 it asked for runs:read.

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.

partner:community:read: no REST endpoints, but MCP server tools that ask for it.

rag:read and rag:write are deprecated: they are not offered when issuing a new key or registering an OAuth app, and they unlock no calls. Keys that already carry them keep working as before; you can drop them the next time you issue the key.

Running Mets

ScopeAllowsEndpoints
agents:readView Mets6
agents:writeCreate and configure Mets12
runs:executeRun Mets — spends Energy2
runs:readRead runs and history2
approvals:readView actions awaiting approval and how they ended (cannot decide)2
routines:readView the routines available to the business, with their steps and paths (cannot start them)2
skills:readView skills9
skills:manageCreate, edit and remove skills12
functions:readView AI Functions (code and Met tools)5
functions:writeCreate and edit AI Functions6
rag:readQuery indexed collections — deprecated—
rag:writeIndex content so it can be queried later — deprecated—
flows:writeCreate, edit, duplicate and archive flows6
automations:readView triggers14
automations:writeCreate, edit and delete triggers7
automations:executeFire automations4

Data

ScopeAllowsEndpoints
collections:readRead collections13
collections:writeCreate, edit and delete collections22
items:readRead items10
items:writeCreate, edit and delete items27
items:generateFill an AI field cell or column with AI (uses Energy)2
contacts:readRead CRM contacts14
contacts:writeCreate, edit and delete contacts18
tasks:readRead agentic tasks (the steps a Met runs)7
tasks:writeCreate, edit and delete tasks16
variables:readRead workspace variables5
variables:writeCreate, edit and delete variables6
files:readRead files and the media library2
files:writeUpload and delete files7
agenda:readView bookable services, free slots and bookings, with their notes and video call link5
agenda:writeBook, reschedule and cancel appointments on the connected calendar3
business:readRead business settings: description and business prompt1
business:writeChange the business description and prompt1

Conversations and channels

ScopeAllowsEndpoints
groups:readView the person's groups of people and Mets, with their members and messages (cannot create or change them)3
groups:writeWrite in the person's groups, as that person. A Met replies only when you @mention it1
conversations:readRead WhatsApp and CRM conversations10
conversations:writeCreate and edit chat quick replies3
handoff:manageAutopilot, assignment and operator message4
channels:readChannel status and broadcasts12
channels:sendSend over WhatsApp and broadcasts13
channels:manageCreate and delete WhatsApp templates5
channels:writeConfigure channels: alias and inbound flow of each WhatsApp line. Changes nothing in Meta2

Integrations and platform

ScopeAllowsEndpoints
integrations:readView MCP integrations6
integrations:manageEnable integrations and store their credentials8
integrations:executeRun integration tools1
images:generateGenerate and edit images and video (uses Energy)6
identity:read“Who am I” (GET /me). Every key has it without ticking it1
mcp:useOpen a session on the MCP server3
webhooks:manageInbound webhooks and outbound event subscriptions21
events:readWorkspace events: live and from the log6
reminders:readView reminders1
reminders:writeCreate, edit and delete reminders3
billing:readPlan and usage (there is no billing:write)12
members:readView the workspace team: id, name, email, role and whether active. No access data1
conversions:readConversion status and settings, plus the conversion.sent and conversion.discarded events—
conversions:writeReport a conversion to Meta (CAPI)2
sites:readView static sites and their versions2
sites:writePublish static sites and switch their live version4
pages:readView your Meteor Web sites (sites and presentations), their links and the text of their pages, without the design2
pages:writeCopy a site as a draft and change the text of unpublished pages, without touching the design2
snapshots:readBrowse the template catalog and view your own2
snapshots:installInstall a template into the workspace2

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.

ScopeAllowsEndpoints
channels:connectConnect the WhatsApp of an attributed account from your platform (single-use session)1
inbox:embedOpen the WhatsApp inbox of an attributed account inside your platform (single-use session)2
snapshots:publishPublish a template to the marketplace1
workspaces:provisionCreate client accounts3
workspaces:rechargeTop up energy on an account you provisioned1
workspaces:closeRequest and revert the closure of an attributed account3
partner:billing:manageThe partner's payment method, used to pay for the accounts it manages2
partner:clients:readAttributed clients3
partner:clients:pausePause an attributed account2
partner:leads:readView leads4
partner:leads:writeCreate, edit, move and delete leads7
partner:projects:readView implementation projects5
partner:projects:writeCreate and edit projects, tasks, milestones and phase progress10
partner:commissions:readCommissions (always read-only)1
partner:payouts:readPayouts (always read-only)2
partner:support:readYour own tickets and your clients'3
partner:support:writeOpen tickets, reply and change status3
partner:community:readView the Partner Community's weekly session—

What test mode does and does not do

A met_test_ key cannot exercise scopes with real external effects: integrations:execute, images:generate, channels:send, conversions:write, channels:manage, workspaces:provision, workspaces:recharge, workspaces:close, partner:billing:manage, snapshots:install, sites:write, channels:connect, inbox:embed, agenda:write, partner:clients:pause and items:generate 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 EnergyNot debited: the run is recorded at zero cost.
How many runsThere 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 effectsThe scopes above answer 403 test_mode_restricted.
livemodefalse on the run resource and on the events that run produces.
Where the data livesIn the same database and the same tables as production. There is no copy, no mirror, no separate environment.
Contacts, items, collections, tasks, filesReal. If the key carries items:write, your tests create items your team sees in the dashboard.
UndoNone. 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:

  1. Issue the test key with read scopes only (plus runs:execute if 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.
  2. 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.
  3. 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.