An automation runs when a record changes. As an org developer you attach one to an entity lifecycle event in API Studio; it then fires for every workspace in your org whenever a matching write happens.
Define an automation
You pick the entity, the operation (create or update), the stage (after runs once the write has succeeded; before can stop the write, see below), an event type, and a destination config. Optional entry_conditions gate when it fires: each is a field, an operator (=, !=, contains, exists, >, >=, <, <=, begins_with) and a value, and all of them must match. = and != compare strictly, so a number field 50 does not equal the text "50".
POST /api/event
{
"entity_name": "Contact",
"event_name": "contact-created-push-salesforce",
"event_type": "external.connector",
"event_stage": "after",
"event_optype": "create",
"enabled": true,
"destination_config": {
"connector_id": "salesforce",
"action_id": "create-contact",
"field_mapping": { "Email": "{{source.email}}" }
}
} {{source.*}} is the changed record
In a destination config, {{source.field}} references the entity record that triggered the event. The mapping is resolved to concrete values before the handler runs. A webhook url and an S3 key_template or metadata value do not read the record directly: map the field first, then use {{name}} for that mapped name.
Event types
- external.connector — call a registered third-party connector action.
- external.webhook — POST to a URL you specify directly.
- external.s3 — write an object to the tenant's own S3 bucket.
- notification.email / .slack / .sms — send a message.
- app.entity — write to another entity within the platform.
- agent_crew — start an agent crew; the changed record is its input. The crew must be active, and for now it is set up through the API or MCP.
- block — before-events only: stop the write itself (see below).
- custom_api — run a Logic Block. Set
target_entityto the block's api name; the field mapping fills the block's declared inputs (foofor a body field,_query.foofor a query field,_path.foofor a path param). The block can use any step, including a Glyph step.
An event that runs a Logic Block always runs its active version — the one you have published — not a draft, so a change you only tested in the console does not affect events until you publish it. Chaining is capped: when an automation's own write (for example a Logic Block create_entity or update_entity step) triggers another automation, each hop is counted on the written record, and automations stop firing once a record's chain depth reaches 5. Writes made by people or the API are not counted. An automation held back by the cap is recorded in the Debug Log as skipped.
Calling a connector, and seeing what happened
When you save an automation that calls a connector, the platform checks it: the connector and action must exist, the action must be available to automations, and the field mapping must cover the action's required fields. This is the same whether you save from the console, the API or an AI assistant over MCP. Nothing about the connector is stored in the automation: its definition is looked up again each time the automation fires, so the action always runs from the current definition, and your org must have adopted the connector. A workspace that has not set up the connector is skipped, and the Debug Log records it as skipped.
To see what an automation did when it fired, filter the Debug Log by its automation id, or over MCP ask list_event_deliveries. It lists each delivery newest first with its status, the HTTP code and any error message — including a delivery that failed before anything was sent, and an automation that was skipped (because of the chain limit, or a disabled route) with the reason. Entries are kept for about 37 days. An automation whose entry conditions did not match did not run, so it shows nothing.
How it flows at runtime
Automations are driven by change data capture on the data store — your write path stays fast and the automation runs asynchronously.
Before-events vs after-events
After-events (covered here) run asynchronously once a write succeeds. A before-event runs synchronously and can reject a write with 424 — use it for validation that must gate the write. The block type rejects matching records outright (add entry conditions and a reason); external.webhook, external.connector and notification.email can also run before the write and stop it if they fail (set fail_on_error to false to let the write continue instead). Inside a before-event only {{key}} and {{mapped.key}} are replaced, from the field mapping result. A before-event whose entry conditions don't match just doesn't run.