> Source: https://builder.ema.ai/v2/builder-guides/triggers-poll
> Title: Poll Triggers

# Poll Triggers

A poll trigger checks an app on a rhythm you set. Ema does the asking, so the app never needs to know Ema exists — there's no URL to hand over and no secret to share.

The valuable part is what Ema can do with the results once you've told it how. Set up the usual way, it starts a separate run for each item, remembers what it has already seen so the same record isn't handled twice, and turns a batch of forty new records into forty runs rather than one. And when polling has been paused, it can catch up on what it missed.

One step is code: the **fetch script**, which is the part that actually asks the app for data. It's the same shape as a tool call, and it's usually short. Everything else is form-filling.

For help choosing between this and the other kinds of trigger, see [Starting an AI Employee Automatically](/builder/v2/builder-guides/automatic-ai-employee-triggers).

> [INFO]
> **Dashboard AI Employees only.** Poll triggers are available on Dashboard AI Employees. See [Creating a Dashboard AI Employee](/builder/v2/builder-guides/dashboard-ai-employee).

## Before you start

**You need a working connection to the app already set up in Integrations.** This is the one hard prerequisite — you can't attach an app that has no connection, and the **Attach** button stays disabled until there is one. Set it up first, then come back.

Setting up a poll trigger replaces any manual input fields on your AI Employee's **Start**, and the fields you had before can't be recovered. On an AI Employee people currently run by hand, check what's there first.

You'll also want the app's API documentation to hand, specifically the part that answers "how do I ask for things that changed since a given time?" That question shapes the whole fetch script.

> [WARNING]
> **An AI Employee that other AI Employees call can't also have a poll trigger.** The two are mutually exclusive in both directions — whichever you set up second is refused, and nothing is changed. If this AI Employee is called from elsewhere, remove the Agents that call it first.

## Step 1: Type

Choose **Poll**. If you arrived from "Add poll" this is already chosen and the wizard opens on Details — you can still step back to see it.

## Step 2: Details

Name the trigger. It shows up in the triggers list, the runs table, and the delivery log, so make it specific: "HubSpot ticket updates" rather than "Poll 1".

## Step 3: Schedule

How often Ema checks, and how it behaves the first time — plus what happens after a pause.

### How often should Ema check?

Set an interval, or use one of the presets: **5 min**, **15 min**, **30 min**, **1 hour**, **6 hours**, **12 hours**, **Daily**. The default is 15 minutes.

The range is **5 minutes to 7 days**, and it must be a whole number of minutes. Values outside that are rejected rather than quietly adjusted, so you'll always know.

**This is a fixed interval, not a calendar.** "Every 6 hours" works; "Mondays at 9am" doesn't. For calendar-shaped timing use a [scheduled trigger](/builder/v2/builder-guides/triggers-scheduled) instead.

Every check calls the app, so don't check faster than you actually need to — you're spending the app's rate limit as well as your own.

### Starting point

This answers one question: **when this trigger is first switched on, what should the very first check cover?**

-   **New items only** — start from the moment it's enabled. Nothing that already exists will fire.
-   **Existing items too, go back a set amount** — between 1 hour and 30 days. Runs your existing backlog through the workflow, once.

> [DANGER]
> **This applies once, and once only — and the backlog runs are real.** The first successful check uses this setting and then it's locked forever; you can't change your mind afterwards. Think carefully before choosing the backlog option, because those runs _do whatever your workflow does_. If it emails customers or raises tickets, going back 30 days on a busy source means 30 days of emails or tickets, all at once. There's no dry run and no way to preview the count, so if you're unsure, start with **New items only**, confirm the workflow behaves, and set up a separate one-off run for the backlog.

### Catch-up limit

Different question, and a standing one: **if polling is paused or falls behind, how far back may it reach when it resumes?**

Option

What it means

**Auto**

Ten times your interval, with a floor of 7 days. Shown resolved, and it follows the interval if you change it.

**Custom**

Anything from twice your interval up to 90 days. Prefills with the Auto value.

**No limit**

Always catches up in full. After a long pause this may fetch a very large backlog in one go.

Anything older than the limit is skipped — and skipping is permanent, not deferred, so those items never run. It's recorded in the delivery log as a skipped range, which is the only trace you'll get.

The catch-up limit never applies to the very first check, so "starting point 30 days, catch-up limit 7 days" is a perfectly sensible combination.

If you pick **Custom** and later lengthen the interval, a limit that has ended up below twice the interval is rejected rather than quietly adjusted, and the error names both numbers. There's an **Auto** button to hand the decision back if you'd rather not manage it.

## Step 4: Fetch and test

> [WARNING]
> **This step is code, and it needs the app's API documentation.** The script's whole job is to return the list of items to check. What takes the thinking isn't the code — it's knowing how to ask that particular app "what changed since this time", which every provider does differently. Have their API reference open before you start.

### Inputs

Key-and-value pairs the script receives. A value is either typed by you and passed through unchanged, or **dynamic** — filled in by Ema on every check:

Dynamic value

What Ema fills in

**last successful poll**

When the last successful check started, less one minute — so an item the app publishes a moment late still falls inside the next window. Most scripts use this as their "from".

**poll time**

The instant this check started.

**poll interval**

Your interval, in seconds.

The default setup gives you one input using **last successful poll**, which is what most scripts want: "give me everything that changed since then".

### The app being polled

Attach the app under **Integrations**, choosing an app and one of its connections. The first app you attach is the one this trigger polls, and it also decides which starter script you get.

Adding or removing an app is versioned, so it takes effect when you publish.

The connection itself works differently. Once a trigger is published, the connection filling each slot is settled — the edit view shows it read-only, and there's no control for swapping it. If a connection stops working, you repair it on the Integrations page; see [If the connection stops working](#if-the-connection-stops-working). Pointing a live trigger at a genuinely _different_ connection is possible, but it isn't a builder action — ask an administrator.

### The script

The script's job is to call the app and **return the list of items to check for new entries**. That's the entire contract.

You get a starter to work from. In practice that means: if you're polling HubSpot there's a real worked starter; for every other app you get a generic skeleton with the shape sketched in comments and `return []` at the bottom, which you'll replace. **Reset to starter** restores whichever you began with.

What the script can do: call the attached app — over plain HTTP or SOAP — use a handful of bundled libraries for parsing (including CSV and XML), log messages, and pause briefly.

What it can't do: **call a model, call another AI Employee, or reach the Tools catalog.** A fetch script fetches. Anything intelligent happens afterwards, in the workflow the trigger starts.

> [WARNING]
> **Read-only is a convention here, not a guarantee.** Nothing stops a fetch script from calling an endpoint that writes or deletes. Only call endpoints that read — a script that runs every 5 minutes forever is the last place you want a side effect.

> [WARNING]
> **A check may return at most 5,000 items or 2 MiB.** Over that, the check fails as `script_error` — deliberately before the position moves, so nothing is lost: fix the script and the same window is still waiting. **Run test** is capped tighter, at 500 items, and truncates instead of failing, so a test's item count can be a truncation rather than the real number.

### Three ways to ask an app what's new

**1\. Since the last poll.** Pass **last successful poll** as the "from" and **poll time** as the "to", and ask the app what changed between them. This is what the starters do, and the one to reach for: the trigger's position only moves when a check succeeds, so a check that fails is covered by the next one. Because **last successful poll** sits a minute back, the window you ask for is your interval plus that minute — if you pass a `limit` to the app, size it for that, not for the interval alone.

**2\. A fixed look-back.** Ask for "the last 15 minutes" every time and ignore **last successful poll**. Easier to write, but a check that fails or runs late loses whatever fell outside the window, permanently. Don't pair this with **Don't deduplicate** — a look-back re-fetches the same items by design, and with nothing collapsing repeats the same item starts a run on every check.

**3\. The whole current list.** Ask for everything each time and let deduplication decide what's new. Simplest to write and heaviest on the app, so it suits small lists rather than busy ones.

### Run test

Running a test is required before you can publish, and it's a **real** call to the app using your connection.

What it does: executes your draft script, live, and captures a sample of what comes back. What it doesn't do: start any runs, or move the trigger's position — so testing never causes items to be skipped later.

The result shows how long the fetch took, how many items were captured, and any messages your script logged — on success as well as failure, which makes `log.info(...)` the fastest way to see what's happening inside a script.

A few things worth knowing about the sample:

-   **The captured sample is small on purpose** — the first 50 items, or 256 KB, whichever it reaches first. The number shown is the size of the sample, not how many items a real check would sweep up.
-   **Values that look like secrets are masked before being stored.** A field whose name contains "authorization", "token", "secret", "password", or "api\_key" is stored as `***`. Note that last one needs the underscore — a field called `apiKey` or `api key` is **not** masked. Treat masking as a convenience for the stored sample, not a guarantee: if the app returns credentials at all, the right fix is to stop requesting them, not to rely on the field name being spotted.
-   **"The fetch worked, but no item fitted"** is a pass that still blocks you, because the next step builds its pickers from the sample. A genuinely empty result is fine and lets you continue.
-   **Editing the script, the inputs, or the attached apps marks the sample stale** and you'll need to test again.

There's a 15-second cooldown between tests, since each one really does call the app.

If the app is down or not ready, use **Save as draft**. Everything you've entered is kept, and you can finish later from the triggers list.

## Step 5: Events

Two decisions, then going live.

### Item shape

-   **Whole result as one run** — the entire fetch result is one item, so each check starts at most one run.
-   **One run per item (list)** — each element of the list starts its own run. This is the default, and the reason most people use poll triggers.

### Item path

Only relevant for list shape: where the list actually lives in what your script returns.

-   Leave it at **root** if your script returns the array itself.
-   Set a dotted path (like `results`) if your script passes a provider's response straight through, so the array sits inside an object.

If you've run a test, Ema shows you the arrays it found in your sample and how many items each holds.

> [DANGER]
> **This is the single most common thing to get wrong, and it fails quietly.** With the path left at root, a script that returns a wrapping _object_ instead of an array is accepted as **one item** — so every check starts exactly one run carrying the whole page, instead of one run per record. Nothing errors, and the check reports success. If your trigger fires once per cycle when you expected many, check this first: either return the array itself, or name the path where the array lives.

Publishing re-checks the path against your stored sample and blocks if it doesn't hold up — but that check can't help if your test returned no items, since there's no shape to compare against. A trigger tested against an empty source can publish and still be wrong.

### How should items be told apart?

Checks overlap on purpose so nothing is missed. This setting is what stops the overlap causing repeat runs.

Option

What it does

**Specific fields** (recommended)

You name the field(s) that identify an item — usually an id. For "fire again when it changes", add the fields that change plus a last-modified timestamp.

**Entire item**

Keys on the item's whole contents, so a run happens whenever anything in it changes.

**Don't deduplicate**

Every returned item starts a run, repeats included.

> [WARNING]
> **Avoid "Entire item" if your items carry volatile fields.** A timestamp, a view counter, or anything else that changes on its own makes every item look new on every check — so the trigger fires constantly for records that haven't meaningfully changed.

If a field you named is missing from a particular item, that item alone is keyed on its whole contents instead and counts as unique. Ema tells you how many items in your sample are affected. A live preview shows how your sample would behave: how many items, how many runs would fire, how many are duplicates.

### Deduplication window

How long Ema remembers a key: **1 day**, **7 days**, **30 days**, or **Never** — meaning it never forgets, which is the default for poll triggers.

The shortest window allowed is twice your interval — anything less and lingering items would re-run on every check. Presets below that floor are disabled and explain why.

> [WARNING]
> **If you keep _Never_, key on something that identifies the item** — an id. Keying on a value that repeats, like a status, would permanently suppress genuinely new items that happen to share it.

### Go live

Choose who the runs happen as, then decide whether to enable now.

As with every trigger, **two things must be true before anything runs**: the trigger is enabled, _and_ the AI Employee is published. The state shows which you're missing: **Off** → **On, not firing** → **Active**.

**Publishing checks your work.** It won't publish without a passing test matching your current script, inputs, and attached apps — if any of those changed since you tested, you'll be told to test again. It also checks that the **Runs as** person is still an active member and that the connection works, though the connection check lets a publish through when it can't get a definite answer — so a broken connection can still show up later as a failing check rather than at publish time.

## What your workflow receives

Setting the trigger up replaced the manual input fields on **Start** with a single packet, delivered in the same shape by every kind of trigger. Your workflow reads values out of that packet instead of out of fields a person filled in.

Reference it as

What's in it

`trigger_input.body`

One item from your fetch — in list shape, a single element of the list your script returned. This is the content you care about, shaped however the app returned it.

`trigger_input.headers`

Metadata for the item. Usually thin for a poll trigger, since there's no incoming request.

`trigger_input.id`

A unique id Ema assigns this item.

`trigger_input.name`

The trigger's own name.

`trigger_input.received_at`

When Ema picked the item up.

`trigger_input.trigger_id`

Which trigger fired — useful if the AI Employee has more than one.

So if each item your script returns has an `id` and a `subject`, your workflow refers to them as `trigger_input.body.id` and `trigger_input.body.subject`.

**Your captured test sample is the reference for this.** Whatever one item looks like in that sample is what a run receives — so build your workflow against the sample rather than guessing from the app's documentation.

## Changing the schedule later

> [DANGER]
> **Changing the interval does not move the next check.** A trigger set to check daily, changed to every 5 minutes, will still wait out its already-scheduled check — potentially the best part of a day — before the new interval takes effect. The change is saved and real; it just applies from the next check onwards. To make a new interval take effect now, **switch the trigger off and on again**, which schedules the next check immediately.

## Who the runs run as

Runs are performed with one person's access, so the run is checked against their permissions. Pointing a trigger at someone other than yourself is administrator-only, and the change applies from the next check with no republishing.

**Reassigning is how you hand a poll trigger over.** If that person leaves, an administrator changes the **Runs as** user and polling continues — nothing is rebuilt, and the fetch script and its connection are untouched.

## If the connection stops working

When the stored connection fails, the trigger shows **Reconnect needed** and the delivery log records `connection_failed`. Retrying won't fix it — the credential itself is the problem.

**Reconnect the app on the Integrations page.** That's the remedy; there's nothing to repair on the trigger itself.

## Monitoring

Each trigger carries a health verdict — **Healthy**, **Failing**, **Silent**, **Waiting**, **Reconnect needed**, **Needs attention**, **Off**, **Not firing**, **Draft** — plus a plain-language line explaining it, such as "94% of cycles succeeded (47 of 50)" or "No successful poll in the last 7 days". That silence window scales with your interval, so a trigger checking weekly isn't called quiet just because it hasn't run today.

The **delivery log** records every check: when it was due, the window it covered, how long the fetch took, and what came of each item. Items show as **fired**, **deduped**, **failed**, or **pending**.

Check outcomes are `ok`, `fetch_failed` (the app rejected or refused the call — usually the first failure you'll meet), `script_error` (your script threw, or returned more than 5,000 items or 2 MiB), `connection_failed`, `timeout`, `throttled` (the app rate-limited Ema), `skipped` (with a reason, such as the AI Employee not being published), `crashed`, and `running` for one in progress.

**Items sitting at "pending" right after a successful check is normal.** Starting the runs is a separate step that idles for up to a minute between batches, so a short wait isn't a fault. If they're still pending after a few minutes, then something is wrong.

## Re-driving a failed item

If an item was fetched but failed to start its run, an administrator can **Re-drive** it from the delivery log. That creates a new run from the item Ema already stored — **the app is not called again**, so you're not at risk of duplicating anything on their side.

Only items that actually failed can be re-driven.

## Turning it off, notes, and deleting

**Switching off** stops the checking. Ema keeps the position it had reached and any failure history, and items already fetched but not yet run are held rather than dropped — they resume when you switch it back on. Switching on again also schedules the next check immediately.

**Notes** let you leave a message for whoever looks after this next — setup quirks, who to ask. They save immediately and don't affect how it runs.

**Deleting** stops polling immediately and asks you to type the trigger's name to confirm. Runs, receipts, and the delivery log are all kept, so the history stays intact — but **any fetched items that haven't run yet are dropped and never fire**, and the confirmation tells you how many that is. If it's the last trigger on the AI Employee, its **Start** reverts to manual input fields, and your original fields are not restored.

## Troubleshooting

**One run per check instead of one per item.** Almost always the item path. See the warning under [Item path](#item-path) — a wrapping object at the root is accepted as a single item, silently.

**Nothing fires at all.** Check both gates: is the trigger enabled, and is the AI Employee published? "On, not firing" means the second one is missing.

**It fires constantly for items that haven't changed.** You're on **Entire item** and the items contain something volatile, like a timestamp. Switch to specific fields.

**It fired once and never again for a record that keeps changing.** Your identifying fields don't include anything that changes. Add a last-modified field.

**A new interval seems to be ignored.** It applies from the next check, which may be a long way off. Switch the trigger off and on to apply it now.

**Items are missing from the far end of a busy window.** Your script's page limit is probably too small — a check working from **last successful poll** covers your interval plus a minute.

**The trigger keeps failing after a script change.** Failed checks retry quickly, and retrying can't fix a bug in the script. Fix the script, run a test, and publish.

## What's next

-   [Starting an AI Employee Automatically](/builder/v2/builder-guides/automatic-ai-employee-triggers) — choosing between the three kinds of trigger.
-   [Webhook triggers](/builder/v2/builder-guides/triggers-webhook) — if the app can notify Ema, this reacts immediately instead of waiting.
-   [Scheduled triggers](/builder/v2/builder-guides/triggers-scheduled) — for calendar-shaped timing rather than a fixed interval.
-   [Build your first workflow](/builder/v2/builder-guides/build-your-first-workflow) — publishing, without which no check starts a run.
