# Automation: Triggers & Actions

> The trigger-and-action mental model that every automation tool (Zapier, Make, n8n, Power Automate) is built on - learn it once and they all click.


---

# Automation: Triggers & Actions

Every automation tool you've heard of - Zapier, Make, n8n, Microsoft Power Automate, Pipedream - looks different on the surface. Different colors, different pricing, different names for things. Underneath, they are the same machine. Something happens, and then some steps run in response. That's it. Once you see that shape, you stop learning "Zapier" and "Make" as separate skills and start seeing one idea wearing different clothes.

This guide is for the person who keeps copying data between two apps by hand - pasting a new signup into a spreadsheet, forwarding an order email to the warehouse, posting a "we got a lead" message in chat. You know a robot should be doing this. You've maybe opened Zapier, seen a wall of options, and closed the tab. You are not a programmer and you don't want to become one. Good - you don't need to.

We'll move in three phases. First, the core mental model: the trigger (the thing that kicks it off), the action (the work that happens), and how information flows from one step to the next. Then we build a real multi-step flow end to end - with filters so it only runs when it should, branches for "if this, do that," and the fiddly-but-essential job of mapping data between steps. Finally, the part nobody warns you about: what happens when your automation breaks at 2am, runs twice, hits a rate limit, or fails silently - and how to set things up so you find out before your customers do.


---

# Triggers, Actions, and the Flow

Picture a vending machine. You put in money - that's the thing that happens. The machine drops your snack - that's what it does in response. An automation is the same: one event sets it off, and one or more steps run as a result.

The vocabulary is universal, even though each tool gives it a slightly different label:

| Idea | Zapier | Make | n8n | Power Automate |
|------|--------|------|-----|----------------|
| The thing that starts it | Trigger | Trigger module | Trigger node | Trigger |
| The work it does | Action | Action module | Node | Action |
| The whole automation | Zap | Scenario | Workflow | Flow |

Learn the left column and you can read any of them.

## The trigger: the "when"

A trigger is the event you're waiting for. "When a new row is added to this spreadsheet." "When someone fills out this form." "When a payment succeeds in Stripe." "When an email lands in this inbox." Every automation has exactly one trigger, and it always sits at the top.

The trigger doesn't *do* anything. It watches. Its only job is to notice that the event happened and hand off whatever information came with it - the form answers, the payment amount, the email body - to the steps below.

## The action: the "then"

An action is a step that does work. "Add a row to a spreadsheet." "Send a Slack message." "Create a contact in the CRM." "Send an email." A flow can have one action or twenty, and they run in order, top to bottom, like a recipe.

So the smallest possible automation is two steps:

```text
WHEN  a new form response comes in        (trigger)
THEN  add a row to the Google Sheet        (action)
```

Read it out loud as "when this, then that." If you can say your automation as one of those sentences, you can build it.

## How a trigger actually fires: polling vs instant

Here's the one piece of plumbing worth understanding, because it explains a delay that confuses everyone the first time.

There are two ways a trigger learns that its event happened.

**Polling** means the automation tool checks the source on a schedule - "any new rows yet? any new rows yet?" - every few minutes. It's like a kid in the back seat asking "are we there yet" on a timer. Polling is low-tech and works with almost any app, but it's not instant. On most tools the polling interval depends on your plan: it might check every 15 minutes on a free plan and every 1–2 minutes on a paid one. So a polled automation can sit idle for several minutes after the event before it notices.

**Instant** triggers (often called *webhooks* or *real-time* triggers) work the other way: the source app actively pings the automation the moment something happens. No waiting, no checking on a timer. It fires within seconds. The catch is that the source app has to support sending these pings, so instant triggers aren't available for every app or every event.

```text
POLLING:   [tool] --"anything new?"--> [app]   ...wait...   repeat every few min
INSTANT:   [app]  --"hey, this happened!"--> [tool]         the instant it occurs
```

The practical takeaway: if your automation feels slow, check whether its trigger is polled. A few minutes of lag is usually the polling interval, not a bug. For anything time-sensitive - a customer expecting an instant confirmation - prefer an instant/webhook trigger if the app offers one.

## How data flows from step to step

This is the part that makes automations feel like magic once it clicks.

When the trigger fires, it doesn't only say "it happened." It carries a payload - a little bundle of fields describing the event. A new-form-response trigger hands down the name, email, and every answer. A new-Stripe-payment trigger hands down the amount, the customer's email, the date.

Every step *below* the trigger can reach up and grab those fields. When you set up an action like "send a Slack message," you don't type the customer's name - you insert the *Name field from the trigger*, and the tool fills in the real value at run time. Most tools show this as a little token or pill you drop into the text box, something like:

```text
New lead: {{Name}} ({{Email}}) just signed up for {{Plan}}.
```

At run time that becomes "New lead: Dana Okoye (dana@acme.co) just signed up for Pro."

And it chains. Step 2's output becomes available to step 3, step 3's to step 4, and so on. If step 2 creates a CRM contact, step 3 can use the new contact's ID. Each step adds its results to a growing pool of fields that everything downstream can pull from.

```mermaid
graph TD
  T[Trigger: new form response] --> A1[Action: create CRM contact]
  A1 --> A2[Action: send Slack message]
  T -. Name, Email .-> A1
  T -. Name .-> A2
  A1 -. new Contact ID .-> A2
```

That's the whole engine. A trigger that fires and hands down a payload; a chain of actions that each grab what they need and pass their own results forward. Filters, branches, and lookups - the things we add in the next phase - are all built on top of these two pieces. Get this model solid and the rest is detail.


---

# Building a Multi-Step Flow

A two-step "when this, then that" is a fine warm-up, but real work has conditions. Only some events deserve a response. Different inputs need different handling. Data from one app rarely lines up cleanly with the next. This phase walks one realistic flow start to finish and introduces each piece as we hit the need for it.

## The scenario

You run a small online store. When someone buys, you want to:

1. Log every order in a spreadsheet for your bookkeeper.
2. For orders over $200, send the customer a personal thank-you email - not the generic receipt.
3. For wholesale customers, route the order to a different chat channel so the fulfillment team flags it for special packing.

Let's build that.

## Step 1 - the trigger

```text
WHEN  a new order is created in the store
```

The trigger hands down a payload: customer name, email, order total, line items, a customer "type" field (retail or wholesale), and a timestamp. Everything below can pull from these.

## Step 2 - the always-on action

Logging every order has no condition, so it goes right after the trigger.

```text
THEN  add a row to the "Orders" spreadsheet
```

Now the **mapping** work. The spreadsheet has columns, and you tell each column which field to pull from the trigger. This is the unglamorous heart of every automation - pointing outputs at inputs.

```text
Spreadsheet column   <-  Trigger field
Date                 <-  {{Order.Timestamp}}
Customer             <-  {{Order.CustomerName}}
Email                <-  {{Order.Email}}
Total                <-  {{Order.Total}}
```

Mapping is where most beginners stumble, and the fix is mechanical: read the column, find the matching field in the picker, drop it in. If a column has no obvious source, you may need a formatting step (more on that below).

## Filters: stop the flow when it shouldn't continue

The thank-you email is only for orders over $200. A **filter** is a gate: the flow runs up to the filter, checks a condition, and only continues if the condition passes. If it fails, the flow quietly stops right there - no error, nothing wrong, that run is done.

```text
ONLY CONTINUE IF  {{Order.Total}}  is greater than  200
```

Put the filter *before* the thank-you email and *after* the spreadsheet row, so logging always happens but the email is conditional. Order matters: anything above a filter always runs; anything below runs only when the filter passes.

```text
1. Trigger: new order
2. Action:  add spreadsheet row      <- always
3. Filter:  Total > 200              <- gate
4. Action:  send thank-you email     <- only if gate passes
```

## Branching: different paths for different inputs

The wholesale-vs-retail routing isn't a yes/no gate - it's a fork. That's a **branch** (Zapier calls these *Paths*, Make uses a *Router*, Power Automate has *Condition* and *Switch*). One incoming run, two or more possible roads, and the tool picks based on a condition.

```mermaid
graph TD
  T[Trigger: new order] --> R{Customer type?}
  R -->|wholesale| W[Post to #fulfillment-wholesale]
  R -->|retail| F[Filter: Total > 200]
  F --> E[Send thank-you email]
```

Each branch is its own little flow with its own steps. Use a branch when the *steps themselves* differ between cases. Use a filter when you only need to decide whether to keep going at all. A useful rule: filter for "should this continue?", branch for "which version of this should run?"

## Lookups: pulling in data the trigger didn't have

Sometimes a step needs information the trigger never carried. Say your thank-you email should greet the customer by first name and mention their account manager - but the order payload only has an email address, not the account manager.

A **lookup** (often "Find a record" / "Search" actions) fixes this. You add a search step: "find the customer in the CRM where email equals `{{Order.Email}}`." That step returns the full customer record, and now downstream steps can use the account-manager field it found.

```text
3.5  Lookup:  find CRM contact where Email = {{Order.Email}}
4.   Action:  send email, signed from {{Contact.AccountManager}}
```

Lookups are how you stitch two systems together when neither one knows everything. The trigger app knows the order; the CRM knows the relationship; the lookup is the bridge.

## Formatting: making data fit

Raw fields are often the wrong shape. The timestamp comes through as `2026-06-30T14:08:55Z` but your spreadsheet wants `June 30, 2026`. The total arrives as `199.5` and you want `$199.50`. The name is `dana okoye` and you want `Dana Okoye` in the greeting.

Every tool ships **formatting** steps for exactly this - Zapier's *Formatter*, Make's built-in functions, n8n's expressions, Power Automate's expression functions. You insert a small step that takes a messy field and emits a clean one, then map the *clean* version into your action.

```text
Format date:    {{Order.Timestamp}}        ->  June 30, 2026
Format money:   {{Order.Total}}            ->  $199.50
Capitalize:     {{Order.CustomerName}}     ->  Dana Okoye
```

Common formatting jobs: dates, currency and number formatting, capitalization, trimming whitespace, splitting a full name into first/last, and find-and-replace. When an action's output looks "off," a formatting step is almost always the cure.

## The whole thing

```text
1. Trigger:  new order
2. Action:   add spreadsheet row (mapped fields)
3. Branch on customer type
   - wholesale -> post to #fulfillment-wholesale
   - retail    -> Filter: Total > 200
                  Lookup: CRM contact by email
                  Format: name + date
                  Action: send personal thank-you email
```

One trigger, a few actions, a filter to gate, a branch to fork, a lookup to enrich, formatters to tidy. That stack covers the large majority of real automations you'll ever build. Everything fancier is more of the same pieces.


---

# When Automations Break

An automation that works on the day you build it is a demo. An automation you can depend on for a year is a different thing. The gap between them is everything in this phase: the ways flows fail, why they sometimes do the wrong thing instead of nothing, and how to set up so you find out fast. The cruel part is that automation breaks quietly. A human who can't do their job complains. A broken flow stops silently, and you discover it three weeks later when your bookkeeper asks where half the orders went.

## Errors: a step couldn't do its job

The most common failure is a single step erroring out. The CRM was down for a minute. A required field came through empty. An app changed its login and your connection expired. When a step errors, the run usually stops at that step - the actions above it already happened, the ones below never will.

That half-finished state is the thing to watch for. If step 2 logged the order and step 3 (charge, email, whatever) errored, you now have a logged order with no follow-up. Errors don't roll back the steps that already succeeded.

Most tools keep a **run history** (Zapier's *Task History*, Make's *History*, Power Automate's *Run history*) showing every execution, which step it reached, and the exact error. When something's wrong, that log is the first place to look - it tells you which step, which run, and usually why.

## Retries: the tool tries again

For transient hiccups - a service briefly unreachable - automation tools often **retry** automatically. They wait a bit and run the failed step again, sometimes several times with growing gaps between attempts (called *backoff*). Retries are helpful: a one-second outage fixes itself and you never notice.

Retries are also the source of the next problem.

## Duplicate runs and idempotency

Sometimes a flow runs twice for one event. A retry fires the step again after it actually *did* succeed (the tool didn't get the confirmation). A polling trigger double-counts a row. A webhook gets delivered twice - networks do that. The result: two thank-you emails, two charges, two spreadsheet rows for one order.

The defense is a fifty-cent word worth knowing: **idempotency**. An idempotent action is one that's safe to run more than once - running it twice leaves the same result as running it once. "Set the status to paid" is idempotent (setting it to paid twice changes nothing). "Add $5 to the balance" is *not* (run it twice and you've added $10).

You can't always make an action idempotent, but you can guard it:

```text
Before charging the card:
  Lookup: is there already a charge for this order ID?
  Filter: only continue if no charge exists
  Then:   charge
```

That dedupe check - "have I already done this for this exact record?" - is the single most valuable habit for any flow that sends money, emails, or messages. When in doubt, look before you leap.

## Rate limits: too much, too fast

Every app caps how often you can call it - say, 100 requests per minute. Cross the line and it starts rejecting your calls with a **rate limit** error (often shown as HTTP 429). This bites hardest when a flow runs in a burst: you import 5,000 contacts, the flow fires 5,000 times, and the destination app slams the door after the first few hundred.

Signs you've hit one: a flood of failures that all start at the same moment, error messages mentioning "rate limit," "too many requests," or "429." The fixes are about slowing down - process in smaller batches, add a short delay step between calls, or spread a bulk job over time instead of all at once. Some tools throttle for you; many leave it to you to be polite.

## Silent failures: the dangerous kind

The errors above at least announce themselves. The truly dangerous failures make no noise:

- A **filter** quietly stops the flow because a condition you didn't expect failed - that's a non-error stop, so it won't show as a failure even though nothing happened.
- A trigger silently stops firing because the connection expired or the source app changed, so the flow doesn't run *at all* - and a flow that never runs generates no error log.
- A field maps to empty, so you send "Hi ," and an action technically "succeeds" while doing something useless.

None of these page you. You have to go looking. The most reliable trap for a flow that stops firing is a **dead-man's switch**: a separate scheduled check that expects to see activity and alerts you when it *doesn't*. "If no orders were logged in the last 24 hours, message me." Absence is the alarm.

## How to monitor a flow you depend on

Treat anything load-bearing like the small piece of infrastructure it is.

```text
1. Turn on failure notifications.
   Every tool can email/Slack you when a run errors. Enable it. This is the floor.

2. Add an error-handling path for critical steps.
   Make has error handlers; Zapier/Power Automate let you branch on failure.
   At minimum: on error, post to a channel you actually read.

3. Build a heartbeat for silent stops.
   A scheduled "did this run today?" check that alerts on absence,
   not just on errors. Catches the trigger that quietly died.

4. Read the run history weekly for important flows.
   Two minutes of skimming catches the slow-burn problems
   before they become a month of missing data.

5. Guard money and messages with a dedupe lookup.
   Cheap insurance against the double-run that emails a
   customer twice or charges them twice.
```

The mindset shift is this: a flow you rely on is not "set and forget," it's "set and watch." The watching is light - a few notifications and one weekly skim - but it's the difference between catching a broken automation in an hour and explaining to a customer why they got charged twice. Build the flow, then build the thing that tells you when the flow stopped working.
