Skip to main content
An assistant or agent doesn’t have to keep checking Ploti to learn that something changed. It can watch a dataset, one table of official records such as a county’s building permits. Each time the dataset gets a new version, Ploti calls a web address of yours with what changed. Your agent then reads only the rows that were added, changed or removed, such as the new permits someone is waiting for, and acts on them. Watching only notifies. It keeps no copy of the data for you, and it works on any Ploti plan. To keep a full copy of a dataset in your own system instead, use Data Feeds, which has its own events.

Webhooks

A webhook is an https address you control. Ploti sends it a signed POST for each event, the moment it happens.
  • webhooks create takes the address and an optional description. It returns a signing secret once. Keep it with your receiver.
  • Over the REST API or the CLI, it also takes an optional header your receiver requires, such as a bearer key. Ploti sends it with every POST and never shows it again. An assistant cannot set one.
  • webhooks list, webhooks test (sends a signed ping) and webhooks delete manage them.
A member may have 20 webhooks, and each belongs to the member whose key made it. Each POST is JSON {id, type, timestamp, data}, the same shape for every event. Answer 2xx quickly and do the work afterwards. A failed delivery is retried six times, at growing intervals, over about a day and a half. A webhook made for a Data Feeds subscription receives only that subscription’s feed events. Every other webhook receives these three kinds of event:

Watch a dataset

datasets watch takes a dataset id. From then on, each new version sends a dataset.changed event to all your webhooks. datasets unwatch stops it, and datasets watches lists what you watch. Watching a dataset twice changes nothing. A member may watch 100 datasets. Any kind of dataset can be watched, documents included, as long as your workspace’s coverage and plan open it.
  • data.version is the new version: Ploti’s own number for the dataset’s published data, separate from Data Feeds version numbers.
  • data.promotedAt is when it went live.
  • data.counts is what it added, changed and removed.
  • data.provenanceUrl says where its values come from.

Read what changed

datasets changes with the event’s version lists exactly the rows it added, changed, removed or renamed (a record that now carries a new id). The added and changed rows of the latest version come with the row itself. changeLog says what was recorded for the version:
  • recorded: every changed row is listed.
  • counts_only: only the totals were kept. Read the dataset with your own conditions instead.
  • ids_changed, full_refresh or first: the dataset’s ids were replaced, it was reloaded whole, or this is its first version, so there is no row-by-row list. Read it again with datasets_rows.

Verify each delivery

Check the signature before acting on a POST. Ploti signs with Standard Webhooks, whose libraries do this for most languages:
  • webhook-id: the delivery’s id, the same on each retry. To skip an event you’ve already handled, check the id in the body.
  • webhook-timestamp: Unix seconds when it was signed. Reject one more than 5 minutes old.
  • webhook-signature: v1,<base64>, an HMAC-SHA256 of <webhook-id>.<webhook-timestamp>.<raw body> keyed with the base64 bytes after whsec_ in the secret. Compare in constant time. While a secret rotates, several signatures are sent, separated by spaces, and any one matching is enough.

Request events

  • data_request.sourcing, .matched, .checkpoint_released (the first results arrived), .ready, .failed, .cancelled, .paid_source, .cycle_refused, .question (Ploti needs the member’s answer, given in Ploti) and .question_reminder.
  • research_request.report_ready (Ploti’s check reported what data exists and what is missing), .question (the check needs the member’s answer; data.questions has the questions, and requests questions answer sends the answers), .answered (data.answer) and .needs_attention (the research can’t finish on its own).
data.url on a request event opens the request in Ploti.