A poller is a source that calls an API on a schedule and emits events when what it sees is new or has changed. Its request uses the same shape as the API request action.
{"kind": "poll", "name": "status-page",
"poll": {
"request": {"method": "GET", "url": "https://status.example.com/api/v2/components.json"},
"select": {"path": "components", "fields": {"id": "id", "name": "name", "status": "status"}},
"emit": {"type": "status.component", "key": "{{item.id}}", "when": "changed", "compare": ["status"]},
"interval": "PT5M"}}Create it with configure ({"sources": [<the above>]}) or POST /v1/sources/poll.
| Part | What it does |
|---|---|
request | Method, URL, headers, auth ({"bearer": "secret:X"}, {"headers": {...}}, or {"connection": "github"} to reuse a connected source's credential), and a JSON body. Templates: {{row.*}}, {{poll.last_polled_at}}. With {"connection": "github"}, Coordinate uses the GitHub App installation for the account the URL names (/repos/{owner}/…), for any endpoint the app's permissions cover: metadata (stars, releases, topics), contents, pull requests, checks, and Actions. Reads (GET) of public repositories the app isn't installed on use Coordinate's own GitHub credentials, so watching any public repository needs no token. Only a private repository the app can't reach needs {"bearer": "secret:NAME"} with a personal access token. |
for_each | Optional. {sql} over the log: each row (up to 200) fills the request template, so the log decides what to poll. Or {values: [...]} for a fixed list: objects are rows as given, and plain values become {value} ({{row.value}}). |
format | json (default), feed (RSS/Atom entries), or page (the page's visible text). |
select | path to the items in the response. Omitted or empty, it's the whole response: a top-level list is the items, and a single object is one item. entries: true turns an object keyed by name (like npm's bulk endpoint) into one item per key, {key, ...value}. fields: names to item paths, or templates like "https://{{item.url}}". |
derive | Extra boolean fields, each a condition over the fields. |
emit | type (templates allowed), key naming each item, and when: new (keys not seen before) or changed (the compare fields differ from the last emit). occurred_at names the field that holds when it happened; without it, the poll time is used. |
settle | {while: Condition, retry: ["PT10S", ...]}: while a value is still settling, hold the emit and look again on that backoff. |
nudge_on | [{event: Matcher, match: {column: "payload.path"}}]: a matching event polls the rows it is about right away. |
interval | How often to poll everything (PT1M to P7D). |
The first poll is a quiet baseline: what's already there is recorded as history, which never fires triggers. Every event's recorded_at is when Coordinate stored it; occurred_at is when it happened, so for a time window over feed items or other imported history, filter on occurred_at. Feed entries use their published date, and Vercel deployments their creation time. To check a poller now instead of waiting, run coordinate poll <source> (or the poll_source tool).
Built-in pollers
- GitHub readiness (on with the GitHub connector):
for_eachopen pull requests from the log, a GraphQL request per pull request,deriveready,settlewhile mergeability is still being computed, and nudges from pull request, review, check, status, and push events. - Web pages and feeds:
formatfeed or page. - Vercel: the deployments API, one event per deployment and state.