CoordinateDocs
Connect · Pollers
Connect

Reach systems without webhooks: call an API on a schedule and emit events when what it sees changes.

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.

json
{"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.

PartWhat it does
requestMethod, 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_eachOptional. {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}}).
formatjson (default), feed (RSS/Atom entries), or page (the page's visible text).
selectpath 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}}".
deriveExtra boolean fields, each a condition over the fields.
emittype (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.
intervalHow 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_each open pull requests from the log, a GraphQL request per pull request, derive ready, settle while mergeability is still being computed, and nudges from pull request, review, check, status, and push events.
  • Web pages and feeds: format feed or page.
  • Vercel: the deployments API, one event per deployment and state.