Jobs
What a job id is, how to run one with endpointId "job:<id>", how looot picks a provider, and what each refusal means.
A job is the task you want done, not the provider that does it. people.email.find is a job.
people.email.verify is a job. Dozens of providers can each carry an endpoint that does the same
job, and looot ranks them against each other so you do not have to.
You see a job id in two places on a search row: capability is the resolved job id (the same
value as job.id), and sourceCapability is the raw slug the endpoint was registered under. Most
of the time they match. When a provider’s endpoint was filed under an older slug that the catalog
now treats as an alias, capability shows the job it belongs to and sourceCapability shows what
it was actually registered as.
Run a job by its id
Send job:<job id> as the endpointId:
looot run job:people.email.verify \
--input '{"email":"jane.doe@example.com"}' \
--wait{
"endpointId": "job:people.email.verify",
"input": { "email": "jane.doe@example.com" },
"idempotencyKey": "<new unique key>",
"wait": 20
}looot run job:<id> works on CLI 1.1.0 today, since the CLI sends whatever you type as the
positional argument. --fallback and --prefer are not in CLI 1.1.0 yet, so if you want either
of those on a job run, use MCP or REST until the next CLI release.
An alias resolves to its canonical job: job:companies.find runs company.enrich.
How the pick works
looot orders the job’s live providers (in prefer order, balanced by default) and picks the
first one that:
- is not in
fallback.exclude, - can run for your workspace right now,
- has a price,
- your workspace’s route policy allows, and
- accepts your input under its own schema (see Job inputs).
The run that follows is then an ordinary run on that one endpoint: the same validation, quote,
hold and settlement a direct run on that endpoint gets. Sending fallback on a job run does not
change the pick; it lets the walk continue to the job’s other providers, in the same order, if the
picked one misses.
A job run does not turn fallback on by default. If you want looot to try the next provider on a
miss, add fallback yourself. See Fallback.
The run record carries requestedJob:
{
"id": "job:people.email.verify",
"job": "people.email.verify",
"prefer": "balanced",
"pickedEndpointId": "icypeas-email-verify",
"reason": "first of 6 people.email.verify endpoints in rankJob order for prefer \"balanced\"",
"skipped": []
}
skipped lists the rows ranked above the pick that were passed over, each with a reason such as
unavailable, unpriced, blocked_by_policy, or needs_identity with “needs <field>”.
A runs_list row for a job run also carries job (the id you sent, e.g. "job:web.scrape.markdown")
next to endpointId (the provider that was picked).
Refusals
A job run that cannot be run is a failed run at $0, refused before any hold. error.code is
validation_error; the exact reason is in result.code:
result.code |
Meaning |
|---|---|
unknown_job |
No job has that id. The message suggests up to 3 close job ids. |
no_supply_for_job |
No endpoint in the catalog does this job. |
needs_input |
No provider of the job accepts the input you sent. result.needs lists each provider with the fields it needs; error.details.fields names each missing field. |
no_runnable_provider |
The job exists, but nothing can run for this workspace right now (no key, drained, unpriced, or the route policy refuses every candidate). |
invalid_input |
A top-level email, url, domain or phone value is broken. See Job inputs. |
no_supply_for_job and no_runnable_provider are whoseError: "gateway"; needs_input,
unknown_job and invalid_input are whoseError: "customer". no_runnable_provider is the one
retryable case: its hint is “connect your own key, or retry later with a new idempotencyKey”.
What is not there yet
Signed-in looot inspect job:<id> is not supported yet. Inspect an endpoint id instead, for
example one you got back from a search row or a previous run’s requestedJob.pickedEndpointId.