On this page
Guides / Automations

Automations

The *when* of your workspace: wire an event to an action and let Meteor fire it without anyone calling the API.

Updated View .md

An automation joins two things: a trigger_type — the event that wakes it up — and an action_kind — what it does when that happens. It is the *when* of the agentic layer: Functions and flows holds the logic, Tasks holds the procedure, and this decides what sets them off.

Uses the automations:read and automations:write scopes, plus automations:execute to fire one on demand.

The model in one sentence

A type from the catalog, some conditions, and an action. That's all:

const auto = await met.automations.create({
  name: 'Publish when the deal is won',
  trigger_type: 'item.field_changed',
  conditions: { collection_id: 42, field: 'estado', to: 'ganado' },
  action_kind: 'flow',
  flow_id: 'f1e2d3c4-0000-4000-8000-123456789abc',
});

console.log(auto.id, auto.enabled);   // → '…', true
auto = met.automations.create(
    name="Publish when the deal is won",
    trigger_type="item.field_changed",
    conditions={"collection_id": 42, "field": "estado", "to": "ganado"},
    action_kind="flow",
    flow_id="f1e2d3c4-0000-4000-8000-123456789abc",
)

print(auto["id"], auto["enabled"])
curl -X POST https://api.met.meteor.com.co/api/v1/workspaces/7/automations \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Publish when the deal is won",
        "trigger_type": "item.field_changed",
        "conditions": { "collection_id": 42, "field": "estado", "to": "ganado" },
        "action_kind": "flow",
        "flow_id": "f1e2d3c4-0000-4000-8000-123456789abc"
      }'

It is born enabled unless you send enabled: false. The optional fields are description, icon, collection_id, action_config and enabled.

The catalog rules

trigger_type is not free text: it comes from the catalog. Read it before you build anything, because it is also the list of what you can offer your own user.

const catalog = await met.automations.catalog();
catalog = met.automations.catalog()
curl https://api.met.meteor.com.co/api/v1/trigger-catalog \
  -H "Authorization: Bearer $MET_API_KEY"

Every entry carries type (what goes in trigger_type), category, label, description, icon, conditions_schema — what you must fill in —, exposes — the variables the action receives when it fires — and enabled.

enabled: false is not a missing permission. Those are types published in the catalog that the dispatcher does not react to yet. You can read them; creating one answers 400. Filter by enabled before drawing options in your own interface.

The types that fire today, with their required conditions:

trigger_typeFires whenRequired conditions
item.createdA new item lands in a collection.collection_id
item.field_changedAn item field moves to a specific value.collection_id, field, to
item.updatedThe item changes, whatever the field.collection_id
item.deletedAn item is deleted from the collection.collection_id
contact.createdA contact enters the CRM (by hand, through a channel, or over the API).
contact.field_updatedA contact field changes.field, operator
date_timeAn exact instant arrives. Once only.scheduled_date (ISO 8601)
recurrentThe cron pattern comes around.recurrence_pattern
field_datetimeA while before, at, or after a date field.source, field_id, direction
webhook.receivedA POST reaches one of your inbound webhooks.endpoint_slug
manualOnly on request. See the note below.

On create, only the required conditions are checked for presence; the rest of the object travels as is and each type interprets it. If one is missing, the response tells you which: Condición "collection_id" requerida para trigger_type "item.created".

recurrence_pattern takes a five-part cron (0 9 * * 1) or one of these aliases: hourly (on the hour), daily (9:00), weekly (Monday 9:00) and monthly (1st of the month, 9:00). The pattern is evaluated in your workspace's time zone, and if it has none set, in Colombian time.

manual has no API trigger. You can create it, but run-now covers managed processes and recurring flows only (see below). If you need to start something from your code whenever you decide to, run the flow directly with met.flows.run(flowId) or the task with met.tasks.execute(taskId).

What it runs: action_kind

action_kindWhat it doesWhat you must give it
taskRuns a task in the workspace.task_id
flowRuns a published flow.flow_id
ai_fieldFills a collection field using AI.collection_id and action_config.field

If you omit action_kind, task is assumed — and then task_id becomes required.

The flow has to be published. Wiring one that only has a draft answers 400: publish it first with met.flows.publish(flowId) — see Functions and flows. The task or flow also has to belong to your own workspace.

Read, enable, disable and delete

const all = await met.automations.list();
const forOneTask = await met.automations.list({ taskId: 'd4c3b2a1-…' });

const one = await met.automations.retrieve(auto.id);

await met.automations.setEnabled(auto.id, false);              // turn it off
await met.automations.update(auto.id, { conditions: { collection_id: 42, field: 'estado', to: 'perdido' } });

await met.automations.remove(auto.id);
all_of_them = met.automations.list()
for_one_task = met.automations.list(task_id="d4c3b2a1-…")

one = met.automations.retrieve(auto["id"])

met.automations.set_enabled(auto["id"], False)
met.automations.update(auto["id"], conditions={"collection_id": 42, "field": "estado", "to": "perdido"})

met.automations.remove(auto["id"])
curl https://api.met.meteor.com.co/api/v1/workspaces/7/automations \
  -H "Authorization: Bearer $MET_API_KEY"

curl -X PATCH https://api.met.meteor.com.co/api/v1/workspaces/7/automations/$AUTO_ID \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":false}'

setEnabled() is a shortcut for update(): it sends enabled alone and leaves the rest of the configuration untouched.

Two things the listing returns that are worth checking before you conclude something is broken:

When you are not the one who paused it

An automation can come back disabled without anyone touching it. It happens to the ones that run a flow: if a single automation runs away and eats the workspace's daily run ceiling on its own, Meteor pauses that automation and writes down why.

const a = await met.automations.retrieve(id);
if (!a.enabled && a.paused_reason) {
  // the system stopped it; a.paused_reason says why
}

paused_reason: null together with enabled: false means the opposite: a person turned it off. Switching it back on with enabled: true clears the reason and gives it a fresh count.

What matters for your integration: what gets paused is the automation at fault, not the workspace. The rest keep running.

Managed processes

Some rows in the listing arrive with is_system_managed: true. Those are processes Meteor publishes and versions — reports, reminders, tools a Met uses inside a conversation. Their definition is not edited over PATCH, and update() with any field other than enabled answers 400. They cannot be deleted either.

In exchange, they have a surface of their own. Before using it, look at what the row itself declares:

const p = await met.automations.retrieve(id);

p.is_system_managed;              // true
p.capabilities.editable_fields;   // ['schedule', 'flow']
p.capabilities.locked_fields;     // ['handler', 'tool_name', 'templates', 'recipients']
p.capabilities.can_restore;       // true
p.managed_revision;               // 2
p.manual_run;                     // does it accept running on demand?
p.last_run;                       // the latest run, already resolved

The six methods that follow are only for these rows. On an automation you created they answer 400, and that is the contract, not a problem with your key.

Run history

const runs = await met.automations.runs(id, { limit: 50 });
// [{ id, source: 'scheduled' | 'manual', status, run_key, result, error_message, started_at, finished_at }, …]
runs = met.automations.runs(automation_id, limit=50)
curl "https://api.met.meteor.com.co/api/v1/workspaces/7/automations/$AUTO_ID/runs?limit=50" \
  -H "Authorization: Bearer $MET_API_KEY"

Newest first. limit goes from 1 to 100; without it, 20. run_key is the key of the occurrence (for example 2026-08-14): it is what keeps the schedule from running the same thing twice.

For the history of one of your own flows the route is a different one: met.flows.listRuns(flowId).

Run on demand

const { ok, already_processed, run } = await met.automations.runNow(id, {
  input: { periodo: '2026-08-14' },
});
res = met.automations.run_now(automation_id, input={"periodo": "2026-08-14"})
curl -X POST https://api.met.meteor.com.co/api/v1/workspaces/7/automations/$AUTO_ID/run-now \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":{"periodo":"2026-08-14"}}'

already_processed: true means that occurrence had already run and your request duplicated nothing.

The response has two shapes, and mixing them up costs an afternoon:

const flowRun = await met.flows.findRun(run.id);

It answers 400 if the process is paused, if it does not accept running on demand (manual_run is false), or if it is a flow that is not recurring.

Change the schedule

await met.automations.updateSchedule(id, '0 12 * * 1-6');   // 12:00, Monday to Saturday
met.automations.update_schedule(automation_id, "0 12 * * 1-6")
curl -X PATCH https://api.met.meteor.com.co/api/v1/workspaces/7/automations/$AUTO_ID/schedule \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recurrence_pattern":"0 12 * * 1-6"}'

The cron is deliberately narrow: five parts, with a fixed hour and days of the week. Day of month and month must be *; anything else answers 400. And it only applies if capabilities.editable_fields includes schedule — a process that fires on an event has no schedule to change.

Edit without overwriting anyone

const p = await met.automations.retrieve(id);

await met.automations.updateConfiguration(id, {
  expected_revision: p.managed_revision,
  recurrence_pattern: '0 7 * * 1-5',
  flow_id: null,                 // disconnects the follow-up flow
});
p = met.automations.retrieve(automation_id)

met.automations.update_configuration(
    automation_id,
    expected_revision=p["managed_revision"],
    recurrence_pattern="0 7 * * 1-5",
    flow_id=None,
)
curl -X PATCH https://api.met.meteor.com.co/api/v1/workspaces/7/automations/$AUTO_ID/configuration \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision":2,"recurrence_pattern":"0 7 * * 1-5","flow_id":null}'

expected_revision is the revision the row carried when you read it. If someone changed it in between, the call fails with 409 instead of overwriting their change; the SDKs surface it as MetInvalidRequestError with status 409. Re-read the process and try again with the new revision.

Send at least one of the two configurable fields: with neither, it answers 400. flow_id: null is explicit and is sent — it disconnects the flow.

Undo, and see who changed what

await met.automations.restoreConfiguration(id, { expected_revision: p.managed_revision });

const changes = await met.automations.configurationHistory(id, { limit: 20 });
// [{ revision, operation: 'update' | 'restore', changed_by_user_id, before_config, after_config, created_at }, …]
met.automations.restore_configuration(automation_id, expected_revision=p["managed_revision"])

changes = met.automations.configuration_history(automation_id, limit=20)

restoreConfiguration() returns the process to the configuration Meteor published, and only applies if capabilities.can_restore is true. The history goes newest first, with limit between 1 and 100 (without it, 20).

Scheduling a task: use this API, not the task's own

There is an older route for hanging a trigger off a task from the task itself (POST /tasks/{taskId}/triggers). It is still published for compatibility, but what it creates is not tied to a workspace, so it does not show up in GET /workspaces/{workspaceId}/automations and it does not go through the catalog's validation.

To schedule a task today, create an automation that points at it:

await met.automations.create({
  name: 'Qualify leads every Monday',
  trigger_type: 'recurrent',
  conditions: { recurrence_pattern: '0 9 * * 1' },
  action_kind: 'task',
  task_id: task.id,
});

Errors you will see

ResponseWhat causes it
400 trigger_type "…" no existe en el catálogoThe type is not in GET /trigger-catalog.
400 …todavía no está habilitado en producciónThe type exists but came back with enabled: false.
400 Condición "…" requerida para trigger_type "…"A required condition is missing.
400 El flow no ha sido publicadoYou wired a flow that only has a draft.
400 La task no pertenece a este workspaceThe task or flow belongs to another workspace.
400 Esta automatización no tiene historial administradoYou called runs() on an automation of your own.
400 Los procesos administrados solo permiten activar o pausarAn update() with fields other than enabled on a managed process.
409 La configuración cambió en otra sesiónYour expected_revision went stale.

The rest — 401 for a wrong key or workspace, 429 for limits — behaves like everywhere else in the API: see Errors, idempotency and pagination.