Codes
Job statuses, attention reasons, error codes with their HTTP status, warnings, events and the other closed sets.
Every closed set of values the API returns, generated from packages/contracts/src/codes.ts. A client may switch on these values: a new one is added in a new API version and announced in the changelog.
#Job statuses
A job's status. succeeded, failed and canceled are terminal; no further transition happens.
| Status | Terminal | Meaning |
|---|---|---|
queued | The job was accepted and is waiting for a worker. A queued job the dispatcher is holding back carries meta.blocked_reason: insufficient_credits or session_locked. | |
running | A worker is executing the job's program. A parked job returns to running when it is resumed or receives its input. | |
awaiting_input | The job reached an await_input step and is paused until a value arrives through POST /jobs/{id}/input or the hosted resolve page. input_request names the input and its deadline. | |
needs_attention | The job hit something it could not resolve on its own and is parked with a checkpoint until somebody decides. attention carries the reason, evidence, proposals and the resume actions available. | |
succeeded | yes | The job finished and its result can be read: data, raw, warnings and meta.steps. This status is terminal. |
failed | yes | The job ended without a complete result; error says why. This status is terminal. |
canceled | yes | The job was canceled, or aborted from a parked state, before it finished. This status is terminal. |
#Attention reasons
Why a job is needs_attention (attention.reason). See human in the loop.
| Value | Meaning |
|---|---|
obstacle | Something covers the page, such as a dialog, a consent banner or a tour, and Kaabist could not dismiss it. The attention carries dismiss proposals you can apply, optionally remembering them for the site. |
drift | A target moved and the healer's best match scored below the 0.90 confidence threshold. Review the relocation proposal, apply it to this job, and promote it to a new program version if it is right. |
unknown_page | The job landed on a page it does not recognise, or a target or expectation failed on the step's own page. The attention's detail names the selector when there is one. |
session_expired | The session the job uses stopped working and could not be refreshed automatically. Sign in again, through takeover or the session's refresh program, and resume the job. |
side_effect_ambiguous | The job was interrupted inside a step marked side_effect: true, so Kaabist cannot tell whether the action happened. It will not repeat the step; check the target system, then skip the step or abort. |
captcha | The page shows a captcha and the program's policies.captcha is park, or a solver could not finish it. A person can solve it through takeover or the hosted resolve page. |
#Errors
The code of the error envelope, on an API answer and on a failed job's error. Retryable means the same request or job can succeed later without a change on your side.
| Code | HTTP | Retryable | Meaning |
|---|---|---|---|
blocked | 422 | yes | The site refused Kaabist, for example an anti-bot challenge the allowed tiers could not pass, or a captcha under policies.captcha: fail. Running the job again later, or allowing a higher max_tier, may get through. |
timeout | 422 | yes | A step outlived its own budget, the job ran past max_duration_ms, or a job parked in needs_attention was never resolved before its deadline. Check error.step and the snapshot to see where it stopped. |
session_expired | 422 | yes | The job's session was no longer signed in, and there was no refresh program to restore it (or the program does not park on an expired session). Refresh the session and run the job again. |
validation_failed | 422 | no | Extracted data broke the program's constraints under on_invalid: fail or policies.strict: true, or a request named a value that is well formed but not acceptable here. The message and details name what did not hold. |
input_expired | 410 | no | The await_input deadline passed before a value arrived, so the job stopped waiting. Posting the input afterwards returns this code; start a new job to try again. |
side_effect_ambiguous | 422 | no | The job could not establish whether a step marked side_effect: true completed, and ended rather than risk doing it twice. Check the target system before you run the job again. |
insufficient_credits | 402 | no | The organisation has no credits available for a new job. Buy credits or enable auto-recharge; jobs already queued wait instead of failing. |
max_credits_exceeded | 422 | no | Continuing would have spent more than the job's max_credits ceiling, so it stopped. Raise max_credits if the job legitimately needs more. |
max_tier_exceeded | 422 | no | The page needs a tier above the job's max_tier, so the job failed instead of escalating. Raise max_tier to let it use the heavier tier. |
max_steps_exceeded | 422 | no | The job reached its max_steps limit (200 by default and at most). Look for a loop or pagination that runs longer than intended. |
ssrf_blocked | 422 | no | A URL resolves to a private, loopback, link-local or metadata address, which Kaabist never connects to. This applies to job targets, webhook endpoints and BYO provider hosts, and is not configurable. |
target_disallowed | 422 | no | The target hostname is on Kaabist's blocklist or on your organisation's own blocklist, so no connection was made. |
byo_proxy_unreachable | 422 | no | Your own proxy failed and the job's proxy fallback is none, so the job failed rather than use an exit you did not choose. details.byo_id names the proxy; fix it, or set fallback: platform. |
byo_proxy_blocked | 422 | yes | The target refused traffic that went through your own proxy. Try again later, or route the job through a different proxy. |
byo_llm_auth_failed | 422 | no | Your own model provider refused its API key. It never fails a job: it appears in a proposal's evidence. Update the key on the BYO provider. |
byo_llm_rate_limited | 422 | yes | Your own model provider rate-limited Kaabist's request. It appears in a proposal's evidence and does not fail the job. |
byo_llm_quota_exceeded | 422 | no | Your own model provider's account has run out of quota. It appears in a proposal's evidence and does not fail the job; top up the account with the vendor. |
byo_captcha_auth_failed | 422 | no | Your own captcha solver refused its API key, so a job with policies.captcha: solve failed. Update the key on the BYO provider. |
byo_captcha_balance | 422 | no | Your own captcha solver account has no balance left, so a job with policies.captcha: solve failed. Top up the account with the vendor. |
byo_provider_failing | 422 | no | The job names a BYO provider whose health checks are failing, so it was refused. details.byo_id and details.path say which; fix the provider or choose another. |
byo_in_use | 409 | no | The BYO provider cannot be deleted while an organisation default or an active schedule still names it. details.org_defaults and details.schedules list them. |
program_invalid | 422 | no | The program failed lint. details.errors[] lists what blocks it and details.warnings[] what is advisory, with lint codes from the DSL reference. |
request_invalid | 400 | no | The request body, query or headers did not match the schema. details.issues[] lists each problem as {path, message}. |
idempotency_conflict | 409 | no | The Idempotency-Key was already used in the last 24 hours with a different request body. Use a new key for a different request. |
session_locked_timeout | 422 | yes | The job waited for its session's exclusive lease and the job holding it was still running a minute later. Run it again once the other job finishes, or use shared concurrency for sites that tolerate it. |
session_locked | 409 | yes | A job currently holds the session's lease, so the session cannot be changed or deleted now. Try again when that job has finished. |
session_engine_mismatch | 409 | no | The job's session was made on one stealth browser (Chromium or Camoufox) and the job would run on the other. A site that ties its sign-in to the browser would refuse it, so the job is refused instead. Drop options.engine, or sign in again on the engine you want. |
job_not_waiting | 409 | no | The action needs a parked job, but the job is not waiting for it, for example input posted to a job that is not awaiting_input, or a resolve page or live session that has ended. |
fence_open | 409 | no | The job is inside a side-effect fence, so it cannot be canceled without risking a half-done action. Wait until the step completes. |
loop_exhausted | 422 | no | A loop_until reached its max iterations without its condition becoming true. Mark the step optional or raise max if that is expected. |
target_ambiguous | 422 | no | A locator matched more than one element. Make the locator more specific or add nth to pick one; details.matches lists the candidates when available. |
canceled | 422 | no | The job was canceled, or aborted from a parked state, before it finished. |
not_found | 404 | no | The resource does not exist, or belongs to another organisation; the two are deliberately indistinguishable. Check the ID and the key's organisation. |
program_not_found | 404 | no | The job's program reference named a program, version or alias that resolves to nothing. Check the name and the alias or version number. |
program_has_schedules | 409 | no | The program still has schedules that point at it. Delete or move those schedules, then delete the program. |
secret_storage_disabled | 422 | no | Secret inputs for a session's refresh program can be stored only when the organisation has enabled secret storage. Enable it, or collect the secret each time through await_input. |
not_implemented | 501 | no | The operation is in the API reference but has not shipped yet. details.owner names the work that will deliver it. |
two_factor_required | 403 | no | An owner or admin without two-factor authentication asked for a privileged action. Enrol a TOTP authenticator at details.enrol_url, then try again. |
reauth_required | 403 | no | A key, organisation or payment-method change needs a password or two-factor code entered within the last 15 minutes. Confirm your identity in the panel and repeat the request. |
rate_limited | 429 | yes | Too many requests in the current window. Wait for Retry-After (also details.retry_after_seconds); details.scope names the bucket (key, org or ip), and details.reason: org_disabled means an operator has disabled the organisation. |
payment_method_required | 422 | no | The request needs a payment method on file: auto-recharge, the customer portal, or a job that uses the browser or stealth tier, evaluate, a BYO provider or a program that signs in. details.rule and details.upgrade_url say what to add when present. |
billing_unavailable | 503 | yes | The billing provider is not configured on this deployment or did not answer, and nothing was charged. Try again later. |
test_quota_exceeded | 429 | yes | The organisation's sk_test_ keys have created their daily allowance of jobs. details.limit, details.used and details.resets_at describe the quota, which resets at midnight UTC. |
plan_required | 403 | no | The organisation's plan does not include what the request needs, such as a tier above the plan's maximum, evaluate, BYO providers or a region. details.rule names it and details.upgrade_url links to a plan that does. |
plan_managed | 409 | no | Kaabist manages this organisation's plan, so no subscription can be bought or changed through checkout. Credit packs can still be bought; contact support to change the plan. |
login_flow_rate_limited | 429 | yes | Programs that fill a password field or a secret input run at most 60 times an hour per organisation. Wait for Retry-After before starting another. |
schedule_interval_too_small | 422 | no | The schedule's cron fires more often than the plan allows. details.min_interval_seconds, details.requested_seconds, details.plan and details.upgrade_url show the limit and how to raise it. |
live_unavailable | 503 | yes | No browser capacity accepted the live session within 10 seconds. Try again shortly. |
pdf_not_pdf | 422 | no | A file served or downloaded as a PDF does not start like one: there is no %PDF- marker in its first 1024 bytes. Nothing was stored. |
pdf_too_large | 422 | no | The PDF is larger than 64 MB, the limit for any downloaded file. Kaabist stops reading at the limit and stores nothing. |
pdf_malware | 422 | no | Kaabist's malware scanner found a known signature in the PDF. The file was not stored, parsed or shown to any model. |
pdf_scanner_unavailable | 503 | yes | Kaabist's malware scanner did not answer, and a PDF is never passed on unscanned. Nothing was stored; try again shortly. |
pdf_encrypted | 422 | no | The PDF is encrypted or needs a password. Kaabist does not open protected documents. |
pdf_too_complex | 422 | no | The PDF went past a structure limit (pages, objects, decompressed size, nesting) or the time and memory its parsing may use. Nothing was stored. |
pdf_malformed | 422 | no | The PDF could not be parsed, even with the reader's recovery. Nothing was stored. |
pdf_active_content | 422 | no | The PDF launches a program or embeds an executable file, which Kaabist never accepts. Scripts and open actions are only flagged; this code is for launch actions and executables. |
pdf_source_missing | 422 | no | A read_pdf step had no PDF to read: without artifact the page it is on is not a PDF, and with artifact the job stored no artifact by that name. Put a goto to the PDF, or a download of it, before the step. |
internal | 500 | yes | Something failed inside Kaabist. A job that fails this way has its credits refunded; if it persists, contact support with the request_id. |
#Warnings
Entries of warnings[] on a job, a step or a field. A warning never changes status. See validation and warnings.
| Value | Meaning |
|---|---|
null_marker | The page printed a null marker such as -, an em dash character or N/A where the value should be, so the field is null. |
parse_failed | The value was found but could not be parsed as the field's type. The field follows its on_invalid policy and raw keeps the original text. |
out_of_range | The value parsed but broke a constraint, such as min, max, pattern, values, max_items or a cross-field assert. The field follows its on_invalid policy. |
required_missing | A field marked required had no value because no locator matched. |
list_short | A list returned fewer items than its min_items. |
fill_rate_drop | Across the program version's recent runs, this field has stopped filling as often as it used to. detail describes the finding; a healing proposal is probably waiting in the review queue, even if this job's data looks fine. |
escalated | The job moved to a heavier tier during the run. meta.escalations records each move and its reason. |
healed_locator_used | A target had moved, and Kaabist used a relocated locator it matched with high confidence for the rest of this job. A proposal to keep the new locator was filed for review. |
obstacle_dismissed | Kaabist dismissed something covering the page, such as a dialog or consent banner, and continued. detail says how when available. |
field_empty | The field has no value: no locator matched, or the page printed a null marker. |
step_skipped | A step marked optional failed and the job continued without it. message says why it failed. |
cache_hit | The result was served from the shared cache (meta.cache: hit) instead of a fresh fetch. |
byo_fallback_used | Your own proxy was unreachable and the job ran on Kaabist's exits because its proxy fallback is platform. That job's proxy traffic is billed at the platform rate. |
pdf_source_not_extractable | The page was a PDF, so an HTML extract schema found nothing in it and its fields are null. Read PDFs with a read_pdf step, which returns their tables. |
pdf_no_text | The PDF passed the security gate but has no extractable text, which usually means scanned pages. Kaabist does not run OCR. |
pdf_pages_truncated | A read_pdf step stopped at its max_pages: it read count of the document's of pages, so tables on later pages are missing and the reading says truncated: true. |
redirect_not_followed | A field with follow_redirects kept some of its links as the page printed them: the http tier cannot ask a link where it redirects, and one step resolves at most 50. count says how many were kept. |
pagination_incomplete | A paginate step could not move on to the next page after reading some, so it kept the count pages it had read instead of starting over. detail names what failed, such as timeout. |
captcha_inject_navigated | A captcha was solved and injecting its token navigated the page, which is how many sites accept the answer. The step was run again on the page it landed on. |
#Events
The type of a webhook delivery and of an event-log entry; events shows each payload.
| Event | Meaning |
|---|---|
job.queued | A job was accepted, re-queued after a worker lost it, resumed, or escalated to a heavier tier. |
job.blocked | A queued job is being held back because the organisation is out of credits; reason is insufficient_credits. Sent once per blocked stretch. |
job.started | A worker began running the job, sent on its first run only. |
job.awaiting_input | The job paused at an await_input step and needs a value. |
job.needs_attention | The job paused and needs a decision; reason is the attention reason. |
job.succeeded | The job finished and its result can be read. |
job.failed | The job ended without a result; reason is the error code. |
job.canceled | The job was canceled or aborted; reason is canceled. |
batch.completed | Every job of a batch has finished. total is the number of jobs in it. |
proposal.created | Kaabist proposed a fix for review, such as a relocated locator or a dismiss recipe. kind says which. |
proposal.decided | Somebody approved or rejected a proposal; decision says which. |
schedule.run | A scheduled job ended with nothing to report: the result matched the previous run (changed: false), or the job did not succeed (status). |
schedule.changed | A scheduled run's watched fields differ from the previous run's. changed_paths lists up to 50 JSON Pointers and diff_url links to the diff. |
schedule.paused | A schedule was paused, by a person or because the organisation ran out of credits; reason is manual or credits. |
schedule.failing | Five runs of a schedule in a row did not succeed. The schedule keeps running; failure_streak counts the failures. |
session.expired | A stored session stopped working and needs a new sign-in. |
program.drift_detected | A field of a published program version stopped filling, parsing or keeping its length, or the version's canary failed, before your next job notices. Sent once per version, field and kind while the finding stays open. |
credits.low | Allowance consumption crossed 80 % or 95 % this period; level says which. Sent once per level per period. |
credits.exhausted | Nothing more can be consumed: schedules and canaries pause and new jobs are refused. Sent once, until credits arrive. |
credits.recharged | Credits arrived after the organisation ran out, or an auto-recharge completed. Paused schedules and canaries resume. |
byo.unhealthy | One of your own providers (proxy, model or captcha solver) started failing its health checks. |
byo.recovered | A BYO provider that was failing or degraded passed two health checks in a row and is healthy again. |
webhook.disabled | Kaabist switched one of your webhook endpoints off after its last 100 deliveries failed over at least a day. Fix the receiver and set the endpoint's status to active again. |
ping | A test delivery you asked for. It goes only to that endpoint, and no event pattern selects it. |
#Diagnose classes
What Kaabist concluded a page was when a step could not proceed (attention.bundle.diagnose_class, error.details.diagnose.class).
| Value | Meaning |
|---|---|
empty_shell | The document is an empty shell, such as a mount point and a script bundle, with no content yet. Kaabist escalates to a heavier tier. |
placeholder_content | The elements are there but print skeleton values such as a lone dash or Loading. At the http tier Kaabist escalates once; if the browser shows the same, the values are treated as genuinely empty. |
content | The page loaded correctly and the values are genuinely absent, so there is nothing to fix; the program's field policies decide the outcome. |
login_redirect | The page shows sign-in landmarks, so the session has expired. Kaabist runs the session's refresh program if there is one. |
challenge_page | An anti-bot interstitial, such as Cloudflare, DataDome, PerimeterX, Akamai or Imperva, stood in the way. Kaabist escalates toward stealth. |
captcha | The page shows a captcha puzzle. The job follows policies.captcha: park for a person, hand it to a solver, or fail. |
landmarks_ok_target_missing | The page is the expected one but a target no longer matches, which is drift. The healer looks for the moved element. |
landmarks_missing_unknown | The page lacks the landmarks the program expects, so it is not the page the program was written for. The job parks as unknown_page. |
covered_target | The target exists but something covers it, such as a dialog or overlay. The obstacle layer tries to dismiss it. |
transient | A passing failure such as a timeout, a server error, a dropped connection or a proxy error. Kaabist retries with backoff within the job's budget. |
assets_blocked | A page that loads its code at runtime timed out while asset blocking had refused some of its requests. Set options.assets: allow; retrying will not help. |
target_missing | A target matched nothing usable on the step's own page, which did load. The evidence names the selector. |
expect_failed | An expect or wait_for did not hold, or an evaluate threw, on the step's own page. The evidence says which. |
#Tiers
| Value | Meaning |
|---|---|
http | One HTTP request and a parsed document, with no JavaScript. The cheapest tier, used when the page's HTML already contains what you asked for. |
browser | A real Chromium that runs the page's JavaScript. Needed when the page renders its content or the program clicks, types or scrolls. |
stealth | A browser with a harder fingerprint on a residential address, for sites that refuse the other tiers. The most expensive tier, and it needs a paid plan. |
#Modes
| Value | Meaning |
|---|---|
live | Created with an sk_live_ key: jobs run for real and consume credits. |
test | Created with an sk_test_ key: jobs run for real but reserve no credits, are capped by a daily quota, keep their results for 24 hours, and produce events tagged mode: test. |
#Key scopes
| Value | Meaning |
|---|---|
run | Read everything, create and act on jobs, write and publish programs, update aliases, decide proposals, and manage sessions and schedules. |
read | Read-only access to the organisation's resources. |
admin | Everything a run key can do, plus API keys, webhooks, BYO providers, billing and organisation settings. No key can manage members or delete the organisation. |
#Roles
| Value | Meaning |
|---|---|
owner | Full control of the organisation, including deleting it. Owners must enable two-factor authentication for privileged actions. |
admin | Everything an owner can do except delete the organisation, including members, keys, billing and BYO providers. Admins must enable two-factor authentication for privileged actions. |
developer | Builds and runs automations: programs, versions and aliases, jobs, proposals, sessions, schedules and webhooks. Can read everything except billing, and cannot manage keys, members or BYO providers. |
reviewer | Handles jobs that need a person: decides proposals, takes over, resumes and cancels jobs. Cannot see API keys, billing or the audit log. |
viewer | Read-only access to programs, jobs and the organisation's other resources, except API keys, billing and the audit log. |