- A tracked person starts a new job or changes their title or headline — a strong “in motion” signal.
- A tracked person earns a certification or receives an award.
- A tracked person relocates, adds a degree, or lists a new skill.
Every request requires the
x-api-version: 2025-11-01 header and a Bearer
token. Replace YOUR_API_KEY in each example with your API key. This
endpoint is open to all API customers — no per-endpoint grant is needed.Pricing — you pay only for notifications. The first run of every
watch is a free baseline that records each person’s starting
values internally (for diffing only — it isn’t delivered) and never fires. After
that you’re charged per notification — one charge for each watched person that
changed on a run. A run that surfaces nothing costs nothing, and the rate does
not depend on how often you check (
every_hours) — only on the
data-freshness tier you pick (5 credits at the
default 30-day freshness). If your balance is too low, the run is suspended rather
than partially delivered. See Pricing for the canonical rates.How a person entity watcher runs
1
Create the watch
POST your entities (people to watch), a track (what to watch for), a
config (schedule + caps), and one or more notifications channels. The response
returns the full watch object, including its id.2
Baseline run (free)
The first run records an internal snapshot of the fields you’re tracking for
each person — just enough to diff against next time. With no prior snapshot to
compare, the baseline never fires and is never charged, and isn’t itself
delivered — it only establishes “before”. To read a person’s current profile on
demand, use the Enrich APIs.
3
Recurring runs
On your schedule (
every_hours), the watcher re-checks each person, diffs them
against their last snapshot, and delivers a notification for every person whose
track condition just became true. You’re charged 5 credits per notification.An Entity Watcher only fires on a transition — the moment a value crosses
from its previous state to a new one. It never fires on the baseline, and won’t
re-notify about a change it already reported. To read a list’s current state on
demand instead of watching for change, use the
Person Enrich API.
What you can track
Thetrack describes the change that triggers a notification — a tree of condition
leaves, optionally combined with and / or groups.
A leaf has the shape:
Crustdata writes “greater-than-or-equal” as
=> and “less-than-or-equal” as
=< — not >= / <=. Those reversed forms return unknown operator.value is required for the comparison operators and omitted for changed and added.
Combine leaves with a group node — { "op": "and" | "or", "conditions": [ … ] } — to
watch several signals at once. An or group fires if any child fires; an and
group fires only when all conditions hold and at least one just became true.
Common person signals
Examples only — a person profile has 200+ addressable fields, and you can track any of them (any scalar withchanged, any array with added).
field paths address the same profile structure the Enrich API returns, so browse
the full set of trackable fields in the
Person Enrich reference. changed requires a
scalar path and added an array path — mixing them returns a 400 (for
example, "changed" on the array experience.employment_details.current is
rejected; watch basic_profile.current_title instead).Recipes: combine signals
Group leaves withor to fire on any of several signals, or and to require them together. These track blocks are all live-verified.
track field of a create request. Groups nest, so you can mix and/or — e.g. “a title change and a new certification.”
track and fields are different
Two independent knobs, and the distinction matters:
Set them independently. Track one field but deliver many (watch for a title change,
yet receive the full employment history and education), or track many and deliver few.
track decides whether you get a notification; fields decides what’s inside it.
Requesting all fields
fields does not affect pricing — you’re charged per notification at your chosen
data-freshness tier (5 credits at the default), no matter how many
field groups you deliver (see Pricing). There’s no cost reason to
keep the payload thin; request whatever your workflow needs.
There’s no wildcard — to receive the complete record, list every field group your
API key is entitled to. fields is a top-level key (a sibling of track), fixed at
create time. The delivered record uses the same schema as Person Enrich — identical field-group names and nesting — so the example below mirrors the full Person Enrich fields set.
Person — all fields
Some groups require a field-level entitlement on your key (e.g.
certifications).
Requesting a group you aren’t entitled to returns a field-permission error, so list
only the groups your key can access. The group names match the Enrich API — see the
Person Enrich fields reference.Choosing the entities
entities maps an identifier type to a list of values. A single watch holds up
to 10,000 people.
Schedule and limits
Theconfig block controls timing and result caps:
Data freshness
config.refresh_frequency_days sets how fresh the tracked data is kept — how
recently each tracked field is refreshed before a run compares it against the last
snapshot. It is independent of trigger.every_hours: every_hours is how often the
watch checks and notifies; refresh_frequency_days is how up-to-date the data it
checks against is, which is what decides how quickly a real change is detected.
Fresher data is priced higher — the per-notification charge is tiered by the freshness
you choose:
The accepted range is 1–30. A value between two tiers is billed at the fresher
(more expensive) tier. Only fields backed by a refresh asset can be kept fresh this
way; if a tracked field has no backing asset, the create is rejected — drop that field
or the cadence.
Delivery channels
Add one or more channels tonotifications and every change fans out to all of them.
At least one notification channel is required today. Every delivery is also
recorded, so beyond the live push you can re-read a watch’s past runs from the
run-history endpoint.
Quick start
Watch one person and get a webhook whenever they start a new job. This request and response are real.Edit the watched list any time
Unlike a saved search, an Entity Watcher’s list is mutable — add or remove people as your pipeline or contact list changes, without recreating the watch.PATCH the
watch with a new entities object:
PATCH status (active / paused), config, and notifications. A
watch’s track and fields are fixed once created; to change what you watch for,
create a new watch.
Manage your watches
Full request/response for each, verified live.
List your watches — GET /watch/person
List your watches — GET /watch/person
status, page with limit and offset.Get one watch — GET /watch/person/{id}
Get one watch — GET /watch/person/{id}
last_run_at is null until the baseline run completes, then carries the most recent run’s timestamp.Pause or resume — PATCH status
Pause or resume — PATCH status
{ "status": "active" } to resume.Delete a watch — DELETE /watch/person/{id}
Delete a watch — DELETE /watch/person/{id}
Request
204 No Content. Deletion is terminal — the watch cannot be resumed.What a notification looks like
Every fired person carries two things: thechanges array (exactly what moved,
independent of fields) and a record (the person’s current data projected to
the fields you requested — the same shape the Enrich API returns, so with default
fields it’s just basic_profile + social_handles). The person’s identity travels
inside the record as crustdata_person_id.
Two ways to receive these, each with a different envelope:
Pushed to your webhook
When a watch fires, wePOST this body to each channel. The example below is from a
watch created with fields: ["basic_profile", "experience"]:
Webhook POST body
results is a flat list — one object per fired person, each
with its own changes and record. A changed scalar reports as
{ "field": …, "type": "changed", "from": …, "to": … }; an added array as
{ "field": …, "type": "added", "new_elements": [ … ] }.
Pulled from run history
To re-read a past run — or audit exactly what was delivered — the run-history endpoint returns the same content under a different envelope.GET /watcher/watches/{id}/runs/{run_id}/summary
Smoke-test your webhook
Before a real change ever fires, push one sample notification through a watch to verify your receiver end-to-end — signature check, JSON parsing, routing — without waiting for a person to actually move. The test delivers the exact envelope a real run sends (same shape, same signature headers), flagged withmetadata.test: true.
It runs no diff, records no snapshot, costs no credits, and persists nothing.
This request and response are real:
POST. It is the shape of a real notification — only
metadata.test: true and the sample values ("sample_text", 1234, …) mark it
as a test. The changes array below is abbreviated to two entries; the full array
mirrors your watch’s track:
Request headers
Webhook POST body
The sample
record carries placeholder values with the exact shape of a
real notification (every field in the fields groups you requested is present), so
your parser, signature check, and routing see production structure — just not real
person data. A changed scalar reports { "type": "changed", "value": …, "from": …, "to": … } (value is the threshold target for >/< operators, null for a plain
changed); an added array reports { "type": "added", "new_elements": [ … ] }."count": 3 to receive three sample people in one envelope — distinct
crustdata_person_ids and summary.delivered: 3 — so you can exercise how your
receiver iterates results.
The signature is computed exactly as for a real notification — HMAC-SHA256 over
<timestamp>.<raw-body> keyed by your API key, delivered as
x-crustdata-signature: t=<unix>,v1=<hex>. A test that verifies here verifies real
deliveries.Rate limits
Watch-management requests are rate-limited to 10 requests per minute per API key. This bounds bursty create/update loops; steady use is unaffected.Person Discovery Watcher
Need to find new matching people instead of watching a known list? Use the
Person Discovery Watcher.
Person field reference
Browse the full set of trackable
field paths for people.
