Skip to main content
POST

Authorizations

Authorization
string
header
required

API key passed as a Bearer token in the Authorization header.

Headers

x-api-version
enum<string>
default:2025-11-01
required

API version to use. 2025-11-01 is the only accepted value. Send it on every call: it is enforced today on POST /watch/{dataset}/search, where a missing or unrecognized value returns 400, and the other watch routes accept a request without it rather than relying on that.

Available options:
2025-11-01
Example:

"2025-11-01"

Path Parameters

dataset
enum<string>
required

The dataset the watch is built on.

Available options:
person,
company,
job,
social_post
Example:

"person"

Body

application/json

Body for creating a discovery watch. The dataset comes from the path, and the API version from the x-api-version header.

filters is required on an ordinary watch. On a realtime watch (config.is_realtime: true) realtime_filters is what the watch runs on, narrowing_filters narrows what it returned, and filters is refused.

config
object
required

Schedule and per-run caps for a discovery watch.

preview
boolean
default:false

Ask what this watch would do instead of creating it. The request is validated exactly as a create is, nothing is persisted, and no credits are charged or reserved — so a preview works even on an account with no credits left, which is the balance a preview is usually being run to inform.

Answers 200 with the preview rather than 201 with a watch. Everything else in the body means the same thing it means on a create, so preview and create differ by this flag alone.

Example:

true

filters
object

The search filter to re-run each interval. A single leaf or a nested AND/OR group, in the dataset's full search grammar.

Required on an index watch, and refused on a realtime one. A realtime watch's records are new by construction, so asking this tree about one means asking an index that has never seen it, which drops every result. Narrow a realtime watch with narrowing_filters instead.

realtime_filters
object

What the live search is asked for on a realtime watch. Requires config.is_realtime: true, and sending one without the other is a 400 in both directions.

This tree accepts fewer fields and a simpler shape than the dataset's full search grammar. Each leaf follows the leaf schema for the path's dataset: RealtimeConditionPerson, RealtimeConditionCompany, RealtimeConditionJob, or RealtimeConditionSocialPost. Each one lists the fields that dataset's live search accepts and the operators and values each field takes. The tree shape is in RealtimeConditionGroup.

Fields the live search cannot express belong in narrowing_filters, when the dataset that owns them is one this watch may narrow on; the refusal names the block. Values are unchanged: you write the same ones here as you do on an index watch, and the watch translates them for the live search.

Example:
narrowing_filters
object

What narrows a realtime watch, keyed by dataset. Requires config.is_realtime: true, and goes at the top level of the body rather than inside config.

A record the live search just found is too new for its own index to hold, so narrowing asks about the durable entity behind it instead: the author of a post, the company hiring for a job, the person as the index already knows them. Each block is a plain single-dataset tree asked of that dataset's own index, in that dataset's own vocabulary, so a field belonging to another dataset is refused rather than resolved.

A block keeps that dataset's full search grammar, unlike realtime_filters: an or across two different fields, nested and / or groups, and every negation operator all work, because an index answers it rather than the live search. The one restriction is that every leaf must belong to the block's own dataset. The fields and operators are exactly those of that dataset's search endpoint: PersonSearchCondition on POST /person/search for the person block, and SearchCondition on POST /company/search for the company block.

Only person and company may be named, and which of the two depends on the watch: a person or social_post watch may narrow on either, and a company or job watch on company alone. Name one block. A job posting or a social post is the new record itself, so there is no older copy of it to ask about, and a condition about the record the search returned belongs in realtime_filters.

A record whose entity the index does not hold is dropped rather than delivered unchecked. Immutable once set, like realtime_filters.

Example:
sorts
object[]

Delivery order within a run, for example newest first. Sorting does not change which records are new, only the order they arrive in when a run is capped.

fields
string[]

Field groups or field paths to include in each delivered record. Defaults to the dataset's standard projection. Every requested field is checked against the key's entitlements at creation, and fields does not affect what a record costs.

Example:
on
enum<string>[]

Which membership transitions notify you. Only added, records that newly match, is supported today, and it is the default.

Available options:
added
Example:
notifications
object[]

Delivery channels. May be empty for a pull-only watch, which pushes nothing and whose results you read from the run-history endpoints instead.

One delivery channel. Every channel on a watch receives every notification.

overflow_policy
enum<string>
default:redeliver

What happens to new records beyond max_results_per_run. redeliver leaves the overflow unseen so it comes back in a later run. drop marks every new record seen so the overflow never returns.

Available options:
redeliver,
drop
Example:

"redeliver"

Response

The preview, when the body carried preview: true. Nothing was created and nothing was charged.

What a discovery watch would match right now. can_create is stated rather than implied by an empty error, so a caller does not have to infer validity from an absence.

can_create
boolean

Whether this body would be accepted by a create. Every check a create runs has already run, so true means the create will pass and false names the reason in error.

Example:

true

count
integer | null

How many records match right now. The first run marks this whole set seen — permanently — and delivers at most 5 of it, so a broad filter spends its backlog on run one. Narrow it before arming rather than after. null when the index could not be reached.

Example:

1240

sample
object[]

Up to three matching records, each cut to the identity a delivered notification keeps.

counted_against
string | null

Where the count came from, not which tier the watch will run on. A realtime watch is previewed against the index too, because sampling live means running the live retrieval the watch exists to pay for, so its preview is an illustration rather than a forecast: staler, and broader wherever a live-only field has no index column.

Example:

"index"

credits
object

What this watch will be charged, at the settings it was asked about.

error
string | null

Why the watch cannot be created, when can_create is false.

error_field
string | null

The body field the error is attached to, when it belongs to one.