Loops
One conversation run again until it gets there, and the saved designs behind them
On this page(6 sections)
A loop repeats a conversation towards a goal until it converges or gives up. One half is what is running right now; the other is the designs somebody authored once and can point at a different job each time.
GET/loopsEvery loop that has run
The loops this workspace has run, newest first, kept after they end. Why it stopped on the fourth round is the question a loop gets read for, and the round-by-round history is the answer.
What you send
Nothing. Call it as it is.
What comes back
| Field | Type |
|---|---|
loopsEvery loop this workspace has run,… | object[] |
conversationIdThe conversation to loop | string |
goalWhat done means, in your words | string |
promptWhat each round is asked to… | string |
contextHow each round meets the last | "fresh" | "continue" |
output | object |
when kind is "none" | shape |
when kind is "claim" | shape |
when kind is "json" | shape |
fieldsThe shape that answer has to… | object[] |
checksWhat else has to be true,… | object[] |
when kind is "command" | shape |
commandThe command to run in the… | string |
when kind is "judge" | shape |
rubricWhat that judge is asked | string |
modelWhich model judges | string |
maxIterationsHow many rounds before it gives… | integer |
maxSpendUsdA ceiling on what the whole… | number |
stallLimitStop after this many rounds in… | integer |
isolatedWhether it works in the conversation's… | boolean |
agentWhich provider the rounds run on | string |
harnessWhich agentic loop they run on | "native" | "claude-code" |
accountWhich account pays | string |
modelWhich model | string |
actsAsWhich persona the rounds act as | string |
worktreeBasePin the private copy to these… | object[] |
repo | string |
base | string |
autoLandWhether the work merges as it… | boolean |
stateHow it ended, and each of… | "running" | "done" | "exhausted" | "stalled" … (7) |
startedAtWhen it began, in milliseconds | number |
endedAtWhen it ended, in milliseconds | number |
resumedHow many times the sandbox restarted… | integer |
detailWhy it ended, for the endings… | string |
iterationsEvery round, in order | object[] |
nWhich round this was | integer |
atWhen it ran, in milliseconds | number |
outcomeHow the round ended, which is… | "continue" | "done" | "error" |
detailWhat the check said, in its… | string |
costUsdWhat the round cost, in dollars | number |
changedWhether anything on disk moved | boolean |
sessionIdThe session it ran on, and… | string |
curl "$SANDBOX/loops" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.list();POST/loopsRun a conversation until it is done
Starts repeating a conversation towards a goal and answers straight away with the loop as recorded; the work carries on without you. The conversation need not exist yet, so run this until it passes can be the first thing you ever say to a new agent. A conversation already looping is refused.
What you send
| Field | Type | Where |
|---|---|---|
conversationIdrequiredThe conversation to loop | string | body |
goalrequiredWhat done means, in your words | string | body |
promptrequiredWhat each round is asked to… | string | body |
contextrequiredHow each round meets the last | "fresh" | "continue" | body |
outputrequired | object | body |
when kind is "none" | shape | body |
when kind is "claim" | shape | body |
when kind is "json" | shape | body |
fieldsrequiredThe shape that answer has to… | object[] | body |
namerequired | string | body |
typerequired | "string" | "number" | "boolean" | "string[]" | body |
descriptionrequired | string | body |
requiredrequired | boolean | body |
checksrequiredWhat else has to be true,… | object[] | body |
when kind is "command" | shape | body |
commandrequiredThe command to run in the… | string | body |
when kind is "judge" | shape | body |
rubricrequiredWhat that judge is asked | string | body |
modelWhich model judges | string | body |
maxIterationsrequiredHow many rounds before it gives… | integer | body |
maxSpendUsdA ceiling on what the whole… | number | body |
stallLimitrequiredStop after this many rounds in… | integer | body |
isolatedrequiredWhether it works in the conversation's… | boolean | body |
agentWhich provider the rounds run on | string | body |
harnessWhich agentic loop they run on | "native" | "claude-code" | body |
accountWhich account pays | string | body |
modelWhich model | string | body |
actsAsWhich persona the rounds act as | string | body |
worktreeBasePin the private copy to these… | object[] | body |
reporequired | string | body |
baserequired | string | body |
autoLandWhether the work merges as it… | boolean | body |
What comes back
| Field | Type |
|---|---|
conversationIdThe conversation to loop | string |
goalWhat done means, in your words | string |
promptWhat each round is asked to… | string |
contextHow each round meets the last | "fresh" | "continue" |
output | object |
when kind is "none" | shape |
when kind is "claim" | shape |
when kind is "json" | shape |
fieldsThe shape that answer has to… | object[] |
name | string |
type | "string" | "number" | "boolean" | "string[]" |
description | string |
required | boolean |
checksWhat else has to be true,… | object[] |
when kind is "command" | shape |
commandThe command to run in the… | string |
when kind is "judge" | shape |
rubricWhat that judge is asked | string |
modelWhich model judges | string |
maxIterationsHow many rounds before it gives… | integer |
maxSpendUsdA ceiling on what the whole… | number |
stallLimitStop after this many rounds in… | integer |
isolatedWhether it works in the conversation's… | boolean |
agentWhich provider the rounds run on | string |
harnessWhich agentic loop they run on | "native" | "claude-code" |
accountWhich account pays | string |
modelWhich model | string |
actsAsWhich persona the rounds act as | string |
worktreeBasePin the private copy to these… | object[] |
repo | string |
base | string |
autoLandWhether the work merges as it… | boolean |
stateHow it ended, and each of… | "running" | "done" | "exhausted" | "stalled" … (7) |
startedAtWhen it began, in milliseconds | number |
endedAtWhen it ended, in milliseconds | number |
resumedHow many times the sandbox restarted… | integer |
detailWhy it ended, for the endings… | string |
iterationsEvery round, in order | object[] |
nWhich round this was | integer |
atWhen it ran, in milliseconds | number |
outcomeHow the round ended, which is… | "continue" | "done" | "error" |
detailWhat the check said, in its… | string |
costUsdWhat the round cost, in dollars | number |
changedWhether anything on disk moved | boolean |
sessionIdThe session it ran on, and… | string |
curl -X POST "$SANDBOX/loops" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"conversationId":"nightly-changelog","goal":"…","prompt":"Update the changelog for the last five commits.","context":"fresh","output":{"kind":"none"},"checks":[{"kind":"command","command":"pnpm dev"},{"kind":"command","command":"pnpm build"}],"maxIterations":1,"maxSpendUsd":1,"stallLimit":1,"isolated":true,"agent":"…","harness":"native","account":"work","model":"claude-sonnet-4-6","actsAs":"…","worktreeBase":[{"repo":"root","base":"…"},{"repo":"site","base":"…"}],"autoLand":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.start({
"conversationId": "nightly-changelog",
"goal": "…",
"prompt": "Update the changelog for the last five commits.",
"context": "fresh",
"output": {
"kind": "none"
},
"checks": [
{
"kind": "command",
"command": "pnpm dev"
},
{
"kind": "command",
"command": "pnpm build"
}
],
"maxIterations": 1,
"maxSpendUsd": 1,
"stallLimit": 1,
"isolated": true,
"agent": "…",
"harness": "native",
"account": "work",
"model": "claude-sonnet-4-6",
"actsAs": "…",
"worktreeBase": [
{
"repo": "root",
"base": "…"
},
{
"repo": "site",
"base": "…"
}
],
"autoLand": true
});POST/loops/{conversationId}/stopMake this round the last
Means do not start another round, not stop what is running. Somebody watching the sixth round do good work can say this is the last one without throwing that work away. To cut the current round off as well, stop the conversation too.
What you send
| Field | Type | Where |
|---|---|---|
conversationIdrequiredWhich conversation's loop | string | address |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/loops/nightly-changelog/stop" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.stop({
"conversationId": "nightly-changelog"
});GET/loops/designsSaved loop designs
Loops somebody authored once and can point at a different job each time. A saved loop is the same loop with its goal left blank until you type one, not a different feature.
What you send
Nothing. Call it as it is.
What comes back
| Field | Type |
|---|---|
designsSaved loops: the machinery with the… | object[] |
idThe design's id | string |
nameWhat to call it | string |
descriptionWhat it is for, in one… | string |
promptWhat each round is asked to… | string |
contextHow each round meets the last:… | "fresh" | "continue" |
outputWhat it has to produce | object |
when kind is "none" | shape |
when kind is "claim" | shape |
when kind is "json" | shape |
fieldsThe shape that answer has to… | object[] |
checksWhat else has to be true | object[] |
when kind is "command" | shape |
commandThe command to run in the… | string |
when kind is "judge" | shape |
rubricWhat that judge is asked | string |
modelWhich model judges | string |
maxIterationsHow many rounds before it gives… | integer |
maxSpendUsdA ceiling on what it may… | number |
stallLimitStop after this many rounds in… | integer |
curl "$SANDBOX/loops/designs" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.designs();POST/loops/designsCreate or replace a saved loop
Say which of the two you mean, so a name that happens to collide cannot silently overwrite somebody's work. A design that could never finish, with nothing to produce and nothing to check, is refused in the same words an ad-hoc loop would be: catching that at save time is the whole advantage of saving.
What you send
| Field | Type | Where |
|---|---|---|
designrequiredThe design to write | object | body |
idrequiredThe design's id | string | body |
namerequiredWhat to call it | string | body |
descriptionWhat it is for, in one… | string | body |
promptWhat each round is asked to… | string | body |
contextrequiredHow each round meets the last:… | "fresh" | "continue" | body |
outputrequiredWhat it has to produce | object | body |
when kind is "none" | shape | body |
when kind is "claim" | shape | body |
when kind is "json" | shape | body |
fieldsrequiredThe shape that answer has to… | object[] | body |
checksrequiredWhat else has to be true | object[] | body |
when kind is "command" | shape | body |
commandrequiredThe command to run in the… | string | body |
when kind is "judge" | shape | body |
rubricrequiredWhat that judge is asked | string | body |
modelWhich model judges | string | body |
maxIterationsrequiredHow many rounds before it gives… | integer | body |
maxSpendUsdA ceiling on what it may… | number | body |
stallLimitrequiredStop after this many rounds in… | integer | body |
createrequiredWhether you mean to make a… | boolean | body |
What comes back
| Field | Type |
|---|---|
idThe design's id | string |
nameWhat to call it | string |
descriptionWhat it is for, in one… | string |
promptWhat each round is asked to… | string |
contextHow each round meets the last:… | "fresh" | "continue" |
outputWhat it has to produce | object |
when kind is "none" | shape |
when kind is "claim" | shape |
when kind is "json" | shape |
fieldsThe shape that answer has to… | object[] |
name | string |
type | "string" | "number" | "boolean" | "string[]" |
description | string |
required | boolean |
checksWhat else has to be true | object[] |
when kind is "command" | shape |
commandThe command to run in the… | string |
when kind is "judge" | shape |
rubricWhat that judge is asked | string |
modelWhich model judges | string |
maxIterationsHow many rounds before it gives… | integer |
maxSpendUsdA ceiling on what it may… | number |
stallLimitStop after this many rounds in… | integer |
curl -X POST "$SANDBOX/loops/designs" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"design":{"id":"a1b2c3d4","name":"nightly changelog","description":"Runs every night and opens a pull request when anything changed.","prompt":"Update the changelog for the last five commits.","context":"fresh","output":{"kind":"none"},"checks":[{"kind":"command","command":"pnpm dev"},{"kind":"command","command":"pnpm build"}],"maxIterations":1,"maxSpendUsd":1,"stallLimit":1},"create":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.saveDesign({
"design": {
"id": "a1b2c3d4",
"name": "nightly changelog",
"description": "Runs every night and opens a pull request when anything changed.",
"prompt": "Update the changelog for the last five commits.",
"context": "fresh",
"output": {
"kind": "none"
},
"checks": [
{
"kind": "command",
"command": "pnpm dev"
},
{
"kind": "command",
"command": "pnpm build"
}
],
"maxIterations": 1,
"maxSpendUsd": 1,
"stallLimit": 1
},
"create": true
});DELETE/loops/designs/{id}Delete a saved loop
Removes the design. A loop already running from it keeps going on its own terms, because it took a copy of what it needed when it started.
What you send
| Field | Type | Where |
|---|---|---|
idrequiredWhich saved loop | string | address |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X DELETE "$SANDBOX/loops/designs/a1b2c3d4" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.loops.removeDesign({
"id": "a1b2c3d4"
});