On this page
startSearchJob
mutation · in the family Reading your records
What it does
Start a search plan in the background — start at one kind of record, follow its links step by step, and let the platform dig while you carry on; read the matches when it is done.
Start ONE background search — a validated plan run AS YOU, in the background: the SearchPlanInput grammar VERBATIM, and the SAME PLAN GATE as searchPlanRun refuses BEFORE any read — an unknown family, a field outside its roster, a hop that is not a relationship of the current family — naming the legal words; a family you may not list refuses the whole plan. The job records the plan (planJson) and its words, runs ONE bounded step at a time (pages of 1000; at most 50000 records examined — the estimate refuses a plan that cannot fit, with the numbers; 300 s from the start → expired WITH the partials; at most 400 steps), and lands its matches as ONE result set for 7 days (read them with searchJobMatches). ONE running job per signed-in session — a second start refuses CONFLICT/SEARCH_JOB_ACTIVE naming the holder: wait for it to finish or cancel it. Where no machine is wired the same steps run inline and the job comes back finished. A JSON null on an optional input field reads as absent. Requires the unrestricted capability on a USER session (an API key or a collaborator refuses AUTHZ/FORBIDDEN — the job is yours: your user, your session).
What happens
It runs as you, inside your organization, over records you may already list; a plan naming a kind you cannot list is refused before anything is read. One background search runs per sign-in at a time — a second start is refused until the first finishes or you cancel it. A search stops on its own after five minutes or fifty thousand records examined, keeping what it found so far. Nothing else changes.
Who may call it
Capability area: Reading your records — Looking up and listing the records of your organization.
- Owner
- System Administrator
- Manager
- Associate Manager
- Warehouse Associate
- Sales Associate
- An API key whose scope allows
api:startSearchJob
Arguments
| Name | Type | Required | Notes |
|---|---|---|---|
plan | SearchPlanInput SearchPlanInput! | yes | No further notes. |
Returns
SearchJob SearchJob! — the budget rule: at most 50000 records examined (the estimate refuses a plan that cannot fit, with the numbers) · 300 s from the start (past it the job is expired WITH its partials) · 400 machine steps. ONE running job per signed-in session (the SEARCHLOCK marker — a second start refuses CONFLICT/SEARCH_JOB_ACTIVE naming the holder). Lifecycle queued → running → done | cancelled | expired | failed: cancel is the owner's (cancelSearchJob) or the session's end; done stays listed, the other terminals are doomed-class. The generic wire is READ-ONLY (the two reads) — the bespokes own the writes: startSearchJob (THE PLAN GATE, then the per-family list-op offer AS THE CALLER — a family you may not list stops the plan before any read) · cancelSearchJob · searchJobMatches (gated by the READER's own list right over the plan's terminal family).
Example request
mutation ExampleStartSearchJob($plan: SearchPlanInput!) {
startSearchJob(plan: $plan) {
id
sysId
type
caption
status
parentId
rootId
createdAt
updatedAt
revisionNum
revision
userId
sessionId
planJson
words
stepsDone
examined
matchCount
startedAt
finishedAt
problem
sfExecutionArn
resultTtl
}
}
Variables:
{
"plan": {
"start": {
"family": "<family>",
"filter": {
"clauses": [
{
"field": "<field>",
"op": "<op>",
"values": [
"<values>"
]
}
]
}
},
"hops": [
{
"direction": "outbound",
"field": "<field>",
"family": "<family>",
"filter": {
"clauses": [
{
"field": "<field>",
"op": "<op>",
"values": [
"<values>"
]
}
]
}
}
]
}
}
Send it with the envelope naming the version: "extensions": {"at": {"version": {"name":"genesis","number":0}}}.
Example response
{
"data": {
"startSearchJob": {
"id": "01900000-0000-7000-8000-37386ae00000",
"sysId": "SJ-EXMP-0000-000F",
"type": "SearchJob",
"caption": "Blue jeans",
"status": "queued",
"parentId": "01900000-0000-7000-8000-065235280000",
"rootId": "01900000-0000-7000-8000-a093dd800000",
"createdAt": "2027-01-31T00:00:00.000Z",
"updatedAt": "2027-01-31T00:00:00.000Z",
"revisionNum": 1,
"revision": "01900000-0000-7000-8000-b7960e180000",
"userId": "01900000-0000-7000-8000-11f967df0000",
"sessionId": "01900000-0000-7000-8000-be25a35a0000",
"planJson": "<plan json>",
"words": "<words>",
"stepsDone": 1,
"examined": 1,
"matchCount": 1,
"startedAt": "2027-01-31T00:00:00.000Z",
"finishedAt": "2027-01-31T00:00:00.000Z",
"problem": "<problem>",
"sfExecutionArn": "<sf execution arn>",
"resultTtl": 1
}
},
"extensions": {
"at": {
"callId": "01EXAMPLE-CALL-ID",
"version": {
"requested": {
"name": "genesis",
"number": 0
},
"serviced": {
"name": "genesis",
"number": 0
}
}
}
}
}
Errors this call can answer
VALIDATION/INVALID— Something in the request is not valid. (VALIDATION)AUTHN/REQUIRED— Sign in to do this. (AUTHN)AUTHZ/FORBIDDEN— Your role does not allow this action. (AUTHZ)RATE_LIMIT/THROTTLED— Too many requests in a short time. (RATE_LIMIT)CONFLICT/*— The record’s state, or a change made in the meantime, does not allow this; the codes are on the CONFLICT page. (CONFLICT)VALIDATION/VERSION_REQUIRED— The request did not say which app version it came from. (VALIDATION)