[{"data":1,"prerenderedAt":503},["ShallowReactive",2],{"-docs-reference-api":3,"-docs-reference-api-surround":492},{"id":4,"title":5,"body":6,"description":482,"extension":483,"links":484,"meta":485,"navigation":486,"path":488,"seo":489,"stem":490,"__hash__":491},"docs\u002Fdocs\u002F4.reference\u002F1.api.md","API reference",{"type":7,"value":8,"toc":469},"minimark",[9,23,28,33,62,69,73,88,99,103,123,147,151,231,235,244,247,251,275,299,303,405,418,422,444,448,465],[10,11,12,13,17,18,22],"p",{},"Transport: HTTPS REST, bearer token, tenant-scoped. ",[14,15,16],"strong",{},"All client input is untrusted"," — the server re-parses raw stdout, computes fingerprints itself, and echoes ",[19,20,21],"code",{},"prompt_hash"," for integrity.",[24,25,27],"h2",{"id":26},"endpoints","Endpoints",[29,30,32],"h3",{"id":31},"enroll","Enroll",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","POST \u002Fv1\u002Fenroll\n     { enrollment_code }\n  → { client_token, client_id, org_id }\n","http","",[19,41,42,50,56],{"__ignoreMap":39},[43,44,47],"span",{"class":45,"line":46},"line",1,[43,48,49],{},"POST \u002Fv1\u002Fenroll\n",[43,51,53],{"class":45,"line":52},2,[43,54,55],{},"     { enrollment_code }\n",[43,57,59],{"class":45,"line":58},3,[43,60,61],{},"  → { client_token, client_id, org_id }\n",[10,63,64,65,68],{},"Enrollment codes are minted in the web UI or via ",[19,66,67],{},"bun run src\u002Fmint-enrollment-code.ts"," — single-use, expiring.",[29,70,72],{"id":71},"identity-catalog","Identity & catalog",[34,74,76],{"className":36,"code":75,"language":38,"meta":39,"style":39},"GET \u002Fv1\u002Fme       → { org, projects[], models[], entitlements }\nGET \u002Fv1\u002Fmodels   → [{ code, display_name, provider }]   # NO command_template — ever\n",[19,77,78,83],{"__ignoreMap":39},[43,79,80],{"class":45,"line":46},[43,81,82],{},"GET \u002Fv1\u002Fme       → { org, projects[], models[], entitlements }\n",[43,84,85],{"class":45,"line":52},[43,86,87],{},"GET \u002Fv1\u002Fmodels   → [{ code, display_name, provider }]   # NO command_template — ever\n",[10,89,90,91,94,95,98],{},"The model catalog is identity-only; ",[19,92,93],{},"command_template"," is machine-specific and lives in the client's ",[19,96,97],{},"config.toml",".",[29,100,102],{"id":101},"campaigns","Campaigns",[34,104,106],{"className":36,"code":105,"language":38,"meta":39,"style":39},"POST \u002Fv1\u002Fprojects\u002F{id}\u002Fcampaigns\n     { type, git:{branch,commit,remote_url,dirty}, base_commit?, changed_paths?, config_version? }\n  → { campaign_id, config_version_id, status, reused }\n",[19,107,108,113,118],{"__ignoreMap":39},[43,109,110],{"class":45,"line":46},[43,111,112],{},"POST \u002Fv1\u002Fprojects\u002F{id}\u002Fcampaigns\n",[43,114,115],{"class":45,"line":52},[43,116,117],{},"     { type, git:{branch,commit,remote_url,dirty}, base_commit?, changed_paths?, config_version? }\n",[43,119,120],{"class":45,"line":58},[43,121,122],{},"  → { campaign_id, config_version_id, status, reused }\n",[124,125,126,127,130,131,134,135,138,139,142,143,146],"note",{},"The server has ",[14,128,129],{},"no source tree"," → the client ",[14,132,133],{},"must"," send ",[19,136,137],{},"changed_paths"," for incremental campaigns. The server intersects them with pass-scope globs + risk triggers. Paths travel, contents do not. Dedup identity is keyed by ",[19,140,141],{},"(project, branch, commit, type)"," so re-creating the same campaign ",[14,144,145],{},"reuses"," it.",[29,148,150],{"id":149},"claim-heartbeat-results","Claim, heartbeat, results",[34,152,154],{"className":36,"code":153,"language":38,"meta":39,"style":39},"POST \u002Fv1\u002Fcampaigns\u002F{id}\u002Fclaim\n     { client_id, models:[...], max_slices, lease_seconds }\n  → { leases:[ { lease_id, slice_id, model_code, pass_code, bucket_code,\n                 prompt:\"\u003Cfull rendered prompt.md>\", prompt_hash, marker,\n                 timeout_seconds, lease_expires_at } ] }\n\nPOST \u002Fv1\u002Fleases\u002F{lease_id}\u002Fheartbeat  → { lease_expires_at }     # best-effort, online clients only\n\nPOST \u002Fv1\u002Fleases\u002F{lease_id}\u002Fresults\n     { exit_code, duration_ms, model_code, client_version, prompt_hash,\n       stdout:\"\u003Craw output.txt>\",   # inline if KB-scale, else presigned upload\n       stderr_tail }\n  → { accepted, parsed_report_count, warnings[] }             # idempotent on (lease_id, prompt_hash)\n",[19,155,156,161,166,171,177,183,190,196,201,207,213,219,225],{"__ignoreMap":39},[43,157,158],{"class":45,"line":46},[43,159,160],{},"POST \u002Fv1\u002Fcampaigns\u002F{id}\u002Fclaim\n",[43,162,163],{"class":45,"line":52},[43,164,165],{},"     { client_id, models:[...], max_slices, lease_seconds }\n",[43,167,168],{"class":45,"line":58},[43,169,170],{},"  → { leases:[ { lease_id, slice_id, model_code, pass_code, bucket_code,\n",[43,172,174],{"class":45,"line":173},4,[43,175,176],{},"                 prompt:\"\u003Cfull rendered prompt.md>\", prompt_hash, marker,\n",[43,178,180],{"class":45,"line":179},5,[43,181,182],{},"                 timeout_seconds, lease_expires_at } ] }\n",[43,184,186],{"class":45,"line":185},6,[43,187,189],{"emptyLinePlaceholder":188},true,"\n",[43,191,193],{"class":45,"line":192},7,[43,194,195],{},"POST \u002Fv1\u002Fleases\u002F{lease_id}\u002Fheartbeat  → { lease_expires_at }     # best-effort, online clients only\n",[43,197,199],{"class":45,"line":198},8,[43,200,189],{"emptyLinePlaceholder":188},[43,202,204],{"class":45,"line":203},9,[43,205,206],{},"POST \u002Fv1\u002Fleases\u002F{lease_id}\u002Fresults\n",[43,208,210],{"class":45,"line":209},10,[43,211,212],{},"     { exit_code, duration_ms, model_code, client_version, prompt_hash,\n",[43,214,216],{"class":45,"line":215},11,[43,217,218],{},"       stdout:\"\u003Craw output.txt>\",   # inline if KB-scale, else presigned upload\n",[43,220,222],{"class":45,"line":221},12,[43,223,224],{},"       stderr_tail }\n",[43,226,228],{"class":45,"line":227},13,[43,229,230],{},"  → { accepted, parsed_report_count, warnings[] }             # idempotent on (lease_id, prompt_hash)\n",[29,232,234],{"id":233},"gate","Gate",[34,236,238],{"className":36,"code":237,"language":38,"meta":39,"style":39},"GET \u002Fv1\u002Fcampaigns\u002F{id}\u002Fgate  → { status: pass|fail|pending, new_findings:[...] }\n",[19,239,240],{"__ignoreMap":39},[43,241,242],{"class":45,"line":46},[43,243,237],{},[10,245,246],{},"Cheap CI poll — no model creds or long runtimes in CI.",[24,248,250],{"id":249},"lease-policy","Lease policy",[10,252,253,254,257,258,262,263,266,267,270,271,274],{},"The client requests ",[19,255,256],{},"lease_seconds"," sized to its batch (server caps it, e.g. 24h). Heartbeats ",[259,260,261],"em",{},"extend"," a lease when the client is online but are ",[14,264,265],{},"not required"," — ",[19,268,269],{},"claim"," and ",[19,272,273],{},"submit"," are the only calls that need connectivity. On expiry the slice requeues.",[276,277,278,286,293],"ul",{},[279,280,281,282,285],"li",{},"A ",[14,283,284],{},"late"," result is still accepted if no other lease has completed the slice.",[279,287,288,289,292],{},"If one has, ",[14,290,291],{},"first-accepted-wins"," and the late output is stored as a provenance artifact.",[279,294,295,296,98],{},"Never double-billed — billing happens once at claim, idempotent per ",[19,297,298],{},"slice_id",[24,300,302],{"id":301},"error-semantics","Error semantics",[304,305,306,322],"table",{},[307,308,309],"thead",{},[310,311,312,316,319],"tr",{},[313,314,315],"th",{},"Status",[313,317,318],{},"Meaning",[313,320,321],{},"Client action",[323,324,325,339,351,364,380,393],"tbody",{},[310,326,327,333,336],{},[328,329,330],"td",{},[19,331,332],{},"200",[328,334,335],{},"accepted",[328,337,338],{},"—",[310,340,341,346,349],{},[328,342,343],{},[19,344,345],{},"202",[328,347,348],{},"accepted-as-artifact \u002F superseded",[328,350,338],{},[310,352,353,358,361],{},[328,354,355],{},[19,356,357],{},"409",[328,359,360],{},"prompt_hash mismatch",[328,362,363],{},"re-claim",[310,365,366,371,374],{},[328,367,368],{},[19,369,370],{},"413",[328,372,373],{},"payload too large",[328,375,376,377],{},"use presigned upload \u002F enable ",[19,378,379],{},"--redact",[310,381,382,387,390],{},[328,383,384],{},[19,385,386],{},"429",[328,388,389],{},"rate limited",[328,391,392],{},"retry with backoff",[310,394,395,400,403],{},[328,396,397],{},[19,398,399],{},"5xx",[328,401,402],{},"server error",[328,404,392],{},[10,406,407,408,410,411,413,414,417],{},"Network errors, ",[19,409,386],{},", and ",[19,412,399],{}," are retried with ",[14,415,416],{},"exponential backoff + jitter"," by the agent.",[24,419,421],{"id":420},"webhooks-optional","Webhooks (optional)",[10,423,424,427,428,431,432,435,436,439,440,443],{},[19,425,426],{},"POST \u002Fv1\u002Fwebhooks\u002Fgithub"," — enabled by ",[19,429,430],{},"GITHUB_WEBHOOK_SECRET",". On ",[19,433,434],{},"pull_request"," opened\u002Fsynchronized it maps the PR's ",[19,437,438],{},"repo_remote_url"," to a project, fetches ",[14,441,442],{},"changed paths"," from the forge API (paths only), and creates an incremental campaign.",[24,445,447],{"id":446},"next-steps","Next steps",[276,449,450,458],{},[279,451,452,457],{},[453,454,456],"a",{"href":455},"\u002Fdocs\u002Fguide\u002Fagent","The agent"," — the client that drives this API.",[279,459,460,464],{},[453,461,463],{"href":462},"\u002Fdocs\u002Freference\u002Fenv-vars","Environment reference"," — server configuration.",[466,467,468],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":39,"searchDepth":52,"depth":52,"links":470},[471,478,479,480,481],{"id":26,"depth":52,"text":27,"children":472},[473,474,475,476,477],{"id":31,"depth":58,"text":32},{"id":71,"depth":58,"text":72},{"id":101,"depth":58,"text":102},{"id":149,"depth":58,"text":150},{"id":233,"depth":58,"text":234},{"id":249,"depth":52,"text":250},{"id":301,"depth":52,"text":302},{"id":420,"depth":52,"text":421},{"id":446,"depth":52,"text":447},"The client ↔ server API surface","md",null,{},{"icon":487},"i-lucide-code-xml","\u002Fdocs\u002Freference\u002Fapi",{"title":5,"description":482},"docs\u002F4.reference\u002F1.api","rxTd8lBxGyStx0i71rXxhDy6-Rf1x5KcD9M49x18-kw",[493,499],{"title":494,"path":495,"stem":496,"description":497,"icon":498,"children":-1},"Air-gap deploy","\u002Fdocs\u002Fdeploy\u002Fairgap","docs\u002F3.deploy\u002F3.airgap","Run TheSpider fully offline with no phone-home","i-lucide-wifi-off",{"title":463,"path":462,"stem":500,"description":501,"icon":502,"children":-1},"docs\u002F4.reference\u002F2.env-vars","Every TheSpider server environment variable","i-lucide-sliders-horizontal",1783955420163]