Discoveries

Get a discovery run

Required permissions: events:read.

THE POLL. Call this until settledAt is non-null, then read the events that stand for the run's scope. settledAt and status: completed arrive together and mean the run is over in full: searched, and every event it produced written. From that moment the candidate and event counts are frozen and GET /v1/events?discoveryRunId= returns the complete scope: everything this run wrote, at any event date, plus the account × signal × lookback matches, including events an earlier run wrote. The counters remain about this execution and are not the size of that page — though a settled run paged to the end yields at least eventsNew, which one page need not. The same read before settlement returns what the run has written so far. There is no timer to wait out.

Two things NOT to poll on. completedAt marks only the first half, the search: it can be set while status is still running, because the events from that search are still being written. And do not watch eventsNew stop changing — a pause between two writes looks exactly like the end of a run and is not one. Read error even when status is completed.

How long to wait. progress says how many accounts are finished and, once there is enough data, etaSeconds and estimatedSettleAt estimate when the run settles. While the run is unsettled the response carries a Retry-After header (and progress.retryAfterSeconds): read the run again after that many seconds rather than on a fixed short timer.

get/v1/discoveries/{runId}

Request

  • Base URL: https://api.trayo.ai
  • URL: https://api.trayo.ai/v1/discoveries/{runId}
  • Auth: HTTP bearer

Path parameters

runIdstring uuid required

Response

The run's current state. Recomputed on every call while the run is live; served from the run's own frozen receipt once settledAt is set.

idstring uuid required
status'running' | 'completed' | 'failed' required

running, completed or failed. This is the POLLING signal and nothing else: it tells you when to stop calling, not what the run achieved. Read error and partial even on a completed run.

completed means everything this run will ever write has been written. It covers both halves of the run: the search across your accounts, and the step that turns what the search found into events. It flips at the same instant settledAt is set, and from that instant the candidate and event counts are frozen and GET /v1/events?discoveryRunId= returns the events that stand for the run's scope: everything it wrote, at any event date, plus matches an earlier run wrote. There is nothing to wait for afterwards.

running covers the write step too, so a run whose search has finished still reads running while its events are being written. That is why completedAt can be set while status is running: the search is done, the run is not. Poll on settledAt (or on this field reaching completed, which is the same moment) and never on completedAt. A run that finds nothing reaches completed exactly as promptly as one that finds a hundred events.

lookbackDaysnumber required
maxResultsnumber nullable required

The requested source-result cap this run was started with, before filtering and deduplication. null means none was sent, so each source used its default. Google News account searches always use a fixed 10-result budget and ignore maxResults; this value is not an event count.

people'add' | 'none' required

The people option this run was started with: add or none. A run started before the option existed reads add, which is what it did.

accountCountnumber required

How many accounts the run was asked to scan.

candidatesScreenedinteger required

How many candidate items this run RETRIEVED and screened: articles, posts and profiles pulled from the sources for your accounts, before anything judged whether they match a signal. It is not a count of events, and these candidates cannot be read as events. candidatesMatched says how many of them matched a signal, eventsNew how many new events the run wrote, and the events themselves are read with GET /v1/events?discoveryRunId=. candidatesScreened: 12 with candidatesMatched: 0 means 12 items were screened and none matched any signal, so this execution wrote no events and there is nothing of its own to read. The same number as the deprecated eventsFound.

Final once settledAt is set, and still moving before that.

candidatesScreened: 0 on a RE-RUN does not mean nothing was searched. Items a signal has already seen are dropped before they are stored, so a second identical run reports 0 even though the same sources were queried and returned the same items: on any later run this number says how much was NEW to that signal, not how much was looked at. To tell "nothing was searched" from "nothing was new", use blockedSignals and error, which do not decay on re-runs.

candidatesMatchedinteger nullable required

How many of the screened candidates matched a signal and went into an event: a new event, or an event already in your workspace that absorbed them. Several matched candidates can make one event, so this is not an event count either; eventsNew is. candidatesMatched above zero with eventsNew: 0 means what matched was already known: the scoped events from GET /v1/events?discoveryRunId= hold it. The sum of bySignal[].candidatesMatched. Final once settledAt is set. Null only on a run settled before this field existed.

eventsFoundnumber required

Deprecated: read candidatesScreened, the same number under a name that says what it counts. Kept, with the same value, for callers that already read it. It counts candidate items retrieved and screened, NOT events: eventsFound: 12 with no events to read means 12 candidates were screened and none matched (candidatesMatched: 0), not 12 events that are missing.

eventsNewnumber required

How many new events THIS RUN WROTE to your workspace. It is the run's execution receipt, not the size of GET /v1/events?discoveryRunId=. That list answers the customer's question instead: every live event this run wrote, whatever its event date, plus every one standing for the run's accounts, signals and lookback window, including events written by earlier runs. It can therefore be non-empty, or longer than this count, when eventsNew is zero — and on a settled run, read to the end, it is never shorter than this count except where an event has since been withdrawn or deleted. One page is capped at 100, so compare against everything nextCursor yields, never against a single page.

The list can also be shorter. A safety net downstream of discovery can withdraw an event after the run ends, and a caller can delete one; the frozen receipt still says the run wrote it. Explicit state=dismissed reads Trayo's withdrawals in the same scope, while caller-deleted events remain absent.

Act on it once settledAt is set, and not before. Until then the run is still writing events and this number is still rising: a value read from an unsettled run is a partial count, not a small result. Once settledAt is set the number is frozen — it is the run's receipt, so it stays what it was even if you later delete some of the events the run created.

eventsNew: 0 on a completed run is a success, not a failure. Results are deduplicated against everything already in the workspace, so RE-RUNNING AN IDENTICAL DISCOVERY IS SAFE: the same accounts, signals and lookback typically return eventsNew: 0 on the second call and create no duplicate events. Do not treat a zero here as a reason to retry. Read the scoped events page, error, and blockedSignals; do not branch on status alone — a completed run always means the work finished, whatever it found.

partialboolean required

True when the results are INCOMPLETE: at least one account × signal check failed (failedChecks lists them), or the run was closed with work still outstanding (error says what). status stays completed on such a run — it says the run stopped, not that every search succeeded. Read failedChecks for which signals and accounts, and do not report eventsNew: 0 on a partial run as "nothing happened". Can turn true while the run is still going; final once settledAt is set.

retry'retry_later' nullable required

retry_later on a settled partial run whose failures were transient (a timeout or source_unavailable check, or work cut short): re-running the same accounts and signals unchanged, later, is the fix. Null otherwise — a clean run needs no retry, a run still going has not finished failing, and a run whose every failure is internal would fail the same way.

startedAtstring date-time required
completedAtstring date-time nullable required

When the SEARCH finished — every account in the run reported back. It is not the end of the run and it is not what to poll on: the events the search found are written afterwards, so this can be set while status is still running. Wait for settledAt. Null until the search finishes, then stable.

settledAtstring date-time nullable required

THE FINISH LINE — when the run became final: the search finished AND every event it produced has been written. Act when settledAt is set. Poll GET /v1/discoveries/{runId} until this field is non-null; at that moment status reads completed or failed, the candidate and event counts are frozen for good, and GET /v1/events?discoveryRunId= returns every live event this run wrote — at any event date, since a job change is dated by the month the job STARTED and can predate the lookback — plus every one standing for the run's account × signal × lookback scope, including events an earlier run wrote. The counts remain this run's frozen receipt. Do not poll on completedAt, and do not try to detect the end by watching eventsNew stop changing — a pause between two writes looks exactly like the end of the run and is not one. Null until the run settles, then stable.

errorstring nullable required

Read this even when status is completed. A completed run with a non-null error ended without doing everything it was asked to — most commonly because none of the requested accounts was eligible (every account must be assigned to a user), which is the single most likely reason a first-time caller sees no new events. The scoped events page can still contain matches written before this run, so this field remains the authority on whether this execution failed.

Changes

No recorded changes to this operation across all 1 revision of this API.