{
 "openapi": "3.1.0",
 "info": {
  "title": "Cloud for Bots API",
  "version": "1.0.0",
  "description": "One API for every Cloud for Bots tool: loop detection, run memory (checkpoint/resume), run budgets, approval rules and scheduled wake-ups for AI agents.\n\n**Auth:** `Authorization: Bearer <key>`. Any one of your keys works for every tool. Free keys: https://cloudforbots.com/offer\n\n**Retries:** read actions retry automatically on 502/503/504. Send `Idempotency-Key` to make writes safe to retry.\n\n**Limits:** 20 requests/second per key (429 + `Retry-After`).\n\n**Webhooks:** signed with `cfb-signature: t=<unix>,v1=<hex>`, where v1 = HMAC-SHA256(secret, t + \".\" + raw body). Retries: 1m, 5m, 30m, 2h, 6h.\n\n**MCP:** https://cloudforbots.com/mcp (Streamable HTTP). **Status:** https://cloudforbots.com/status\n\n**SDKs:** [TypeScript/JS](/sdk/cloudforbots.mjs) ([types](/sdk/cloudforbots.d.ts)) \u00b7 [Python](/sdk/cloudforbots.py)\n\n# Quickstart\n\nGet a free key at [cloudforbots.com/offer](https://cloudforbots.com/offer) (one email, keys show right away). Any one of your keys works for every tool. Keep it in an environment variable:\n\n```bash\nexport CLOUDFORBOTS_API_KEY=mb_your_key_here\n```\n\n## TypeScript / JavaScript\n\nNo install needed: the SDK is one zero-dependency ES module (Node 18+, Deno, Bun, browsers).\n\n```bash\ncurl -o cloudforbots.mjs https://cloudforbots.com/sdk/cloudforbots.mjs\ncurl -o cloudforbots.d.ts https://cloudforbots.com/sdk/cloudforbots.d.ts   # types for TypeScript\n```\n\n```ts\nimport { CloudForBots, CloudForBotsError } from \"./cloudforbots.mjs\";\n\nconst cfb = new CloudForBots(); // reads CLOUDFORBOTS_API_KEY\n\n// 1. After each agent step, ask Loop Breaker if the run is stuck\nconst step = await cfb.loop.checkStep({ run_id: \"invoice-run-42\", tool: \"search_crm\", args: { customer: 4471 }, error: \"timeout after 30s\" });\nif (step.halt) throw new Error(\"Loop detected: \" + step.outcome);\n\n// 2. Save progress so a crashed run can resume\nawait cfb.memory.saveCheckpoint({ run_id: \"invoice-run-42\", step: \"invoice-312\", state: { done: 312 } }, { idempotencyKey: \"invoice-run-42:312\" });\nconst resume = await cfb.memory.resume({ run_id: \"invoice-run-42\" });\n\n// 3. Errors carry the HTTP status, type and request id\ntry { await cfb.approve.checkAction({ action: \"delete 40 customer records\" }); }\ncatch (e) { if (e instanceof CloudForBotsError) console.error(e.status, e.type, e.requestId); }\n```\n\n## Python\n\nStandard library only (Python 3.8+).\n\n```bash\ncurl -o cloudforbots.py https://cloudforbots.com/sdk/cloudforbots.py\n```\n\n```python\nfrom cloudforbots import CloudForBots, CloudForBotsError\n\ncfb = CloudForBots()  # reads CLOUDFORBOTS_API_KEY\n\nstep = cfb.loop.check_step(run_id=\"invoice-run-42\", tool=\"search_crm\", args={\"customer\": 4471}, error=\"timeout after 30s\")\nif step.get(\"halt\"):\n    raise SystemExit(\"Loop detected: \" + step[\"outcome\"])\n\ncfb.memory.save_checkpoint(run_id=\"invoice-run-42\", step=\"invoice-312\", state={\"done\": 312}, idempotency_key=\"invoice-run-42:312\")\nprint(cfb.memory.resume(run_id=\"invoice-run-42\"))\n\ntry:\n    cfb.approve.check_action(action=\"delete 40 customer records\")\nexcept CloudForBotsError as e:\n    print(e.status, e.type, e.request_id)\n```\n\n## Webhooks\n\n```python\nfrom cloudforbots import verify_webhook\n# Flask example: raw bytes, exactly as received\nok = verify_webhook(WEBHOOK_SECRET, request.headers[\"cfb-signature\"], request.get_data())\n```\n\nRegister an endpoint with `cfb.webhooks_add(\"https://you.example/hook\", [\"loop.halted\", \"budget.stopped\"])` (Python) or `cfb.webhooks.add(url, events)` (JS). The secret is returned once. Rotate it with `cfb.webhooks_rotate(url)`: the old secret keeps working for 24 hours, and during that window `cfb-signature` carries two `v1=` values (`verify_webhook` accepts either). See failed deliveries with `cfb.webhooks_deliveries(\"failed\")` and resend one with `cfb.webhooks_redeliver(id)`. Events: `loop.halted`, `budget.stopped`, `approval.held`, `call.failed`, `key.cap_warning`, `key.cap_reached`, `key.expiring`, `key.expired`.\n\n## MCP\n\nPoint any MCP client at `https://cloudforbots.com/mcp` with header `Authorization: Bearer $CLOUDFORBOTS_API_KEY`. Tools are named `loop_check_step`, `memory_save_checkpoint`, `budget_open_budget`, `approve_check_action`, `wake_schedule_wake` and so on.\n\n## Key limits\n\nOn the dashboard (Keys, then Key controls) you can limit a key to some tools or to read-only actions, rotate it, or revoke it. A limited key gets `403 forbidden_scope` outside its limits.\n"
 },
 "servers": [
  {
   "url": "https://cloudforbots.com"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   }
  }
 },
 "paths": {
  "/api/v1/loop-breaker/check_step": {
   "post": {
    "operationId": "loop_breaker__check_step",
    "tags": [
     "loop-breaker"
    ],
    "summary": "check step",
    "description": "Call AFTER each tool call or agent step with what you just did and whether it failed. Loop Breaker fingerprints the call (tool + normalized args + error) and returns verdict ok, loop_detected or halted. Pro: on a loop, halt=true; stop the run, do not retry, and tell your user what repeated. Free watch-only: never halts, returns would_halt and logs it for the weekly report.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "run_id": {
          "type": "string",
          "description": "Any stable id for this agent run, e.g. \"nightly-sync-2026-10-15\". Use the same id for every step of the run (and its sub-agents)."
         },
         "tool": {
          "type": "string",
          "description": "The tool or step name you just ran, e.g. \"browser.click\" or \"git push\"."
         },
         "args": {
          "description": "The arguments you passed (object or string). Volatile values like timestamps and ids are normalized away."
         },
         "error": {
          "type": "string",
          "description": "The error message if the step failed. Omit or empty if it succeeded."
         },
         "max_failures": {
          "type": "integer",
          "description": "Same failing call this many times = loop. Default 3 (min 2, max 20)."
         },
         "max_repeats": {
          "type": "integer",
          "description": "Same call this many times in a row (even if it succeeds) = loop. Default 5 (min 3, max 50)."
         }
        },
        "required": [
         "run_id",
         "tool"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/loop-breaker/loop_status": {
   "post": {
    "operationId": "loop_breaker__loop_status",
    "tags": [
     "loop-breaker"
    ],
    "summary": "loop status",
    "description": "Show a run's loops so far, whether it is halted, and its last 10 steps.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "run_id": {
          "type": "string",
          "description": "Any stable id for this agent run, e.g. \"nightly-sync-2026-10-15\". Use the same id for every step of the run (and its sub-agents)."
         }
        },
        "required": [
         "run_id"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/loop-breaker/weekly_report": {
   "post": {
    "operationId": "loop_breaker__weekly_report",
    "tags": [
     "loop-breaker"
    ],
    "summary": "weekly report",
    "description": "Draft a plain-English report of the last 7 days for your key (or IP): runs, loops caught, wasted repeat steps, and the worst offenders. Read-only; sends nothing. Show it to your user.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/loop-breaker/usage_and_upgrade": {
   "post": {
    "operationId": "loop_breaker__usage_and_upgrade",
    "tags": [
     "loop-breaker"
    ],
    "summary": "usage and upgrade",
    "description": "Show today's usage for your key or IP and the $49/month All Access upgrade link (cloudforbots.com/offer). Free tier: 500 calls/day. Not metered.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/run-memory/save_checkpoint": {
   "post": {
    "operationId": "run_memory__save_checkpoint",
    "tags": [
     "run-memory"
    ],
    "summary": "save checkpoint",
    "description": "Call after each completed step with the state you would need to continue (cursor, ids done, partial results; up to 256 KB JSON). Pro saves it (last 20 per run, kept 30 days). Free watch-only saves nothing and returns would_save: exactly what would have been stored. Never put passwords or API keys in state.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "run_id": {
          "type": "string",
          "description": "A stable id for this job that survives a crash, e.g. \"invoice-import-oct\". Use the same id when you restart."
         },
         "step": {
          "type": "string",
          "description": "The step you just finished, e.g. \"imported page 3 of 10\"."
         },
         "state": {
          "type": "object",
          "description": "JSON state needed to resume."
         },
         "done": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "Optional list of completed item ids, so a resume skips them."
         }
        },
        "required": [
         "run_id",
         "step",
         "state"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/run-memory/resume": {
   "post": {
    "operationId": "run_memory__resume",
    "tags": [
     "run-memory"
    ],
    "summary": "resume",
    "description": "Call FIRST when a run starts. If a checkpoint exists for run_id (Pro), returns the last step, state and done list: continue from there and skip finished work. If none, start fresh.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "run_id": {
          "type": "string",
          "description": "A stable id for this job that survives a crash, e.g. \"invoice-import-oct\". Use the same id when you restart."
         }
        },
        "required": [
         "run_id"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/run-memory/list_checkpoints": {
   "post": {
    "operationId": "run_memory__list_checkpoints",
    "tags": [
     "run-memory"
    ],
    "summary": "list checkpoints",
    "description": "List recent checkpoints (step, size, time) for one run or all runs on your key.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "run_id": {
          "type": "string",
          "description": "A stable id for this job that survives a crash, e.g. \"invoice-import-oct\". Use the same id when you restart."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/run-memory/weekly_report": {
   "post": {
    "operationId": "run_memory__weekly_report",
    "tags": [
     "run-memory"
    ],
    "summary": "weekly report",
    "description": "Draft a plain-English report of the last 7 days: runs, checkpoints saved (Pro) or that would have been saved (free). Read-only; sends nothing.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/run-memory/usage_and_upgrade": {
   "post": {
    "operationId": "run_memory__usage_and_upgrade",
    "tags": [
     "run-memory"
    ],
    "summary": "usage and upgrade",
    "description": "Show today's usage for your key or IP and the $49/month All Access upgrade link (cloudforbots.com/offer). Free tier: 500 calls/day. Not metered.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/open_budget": {
   "post": {
    "operationId": "budget_guard__open_budget",
    "tags": [
     "budget-guard"
    ],
    "summary": "open budget",
    "description": "Open a hard budget for one agent run before it starts: max dollars, tokens, calls and/or wall-clock seconds. Returns a handle. Give the handle to every sub-agent; they all draw from the same ledger, so one runaway loop or fork cannot exceed the run cap.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "label": {
          "type": "string",
          "description": "What this run is for, e.g. \"nightly code cleanup\"."
         },
         "max_usd": {
          "type": "number",
          "description": "Dollar cap for the whole run (your own estimates per call)."
         },
         "max_tokens": {
          "type": "integer",
          "description": "Token cap (input + output) for the whole run."
         },
         "max_calls": {
          "type": "integer",
          "description": "Cap on model/tool calls for the whole run."
         },
         "max_seconds": {
          "type": "integer",
          "description": "Wall-clock cap in seconds from now."
         },
         "ttl_hours": {
          "type": "integer",
          "description": "Handle lifetime (free max 24, Pro max 168). Default 24."
         },
         "mode": {
          "type": "string",
          "enum": [
           "shadow",
           "enforce"
          ],
          "description": "shadow = watch only: track spend and flag where the cap WOULD have stopped the run, never block (free). enforce = return allowed=false at the cap (Pro). Default: enforce on Pro, shadow on free."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/reserve": {
   "post": {
    "operationId": "budget_guard__reserve",
    "tags": [
     "budget-guard"
    ],
    "summary": "reserve",
    "description": "Call BEFORE each model call, tool call or sub-agent spawn with your estimate. Returns allowed=true and deducts it, or allowed=false with the limit that tripped. When allowed is false, stop, do not retry, and return your partial result to the user. Atomic, so parallel sub-agents cannot both slip under the cap.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "handle": {
          "type": "string",
          "description": "The budget handle from open_budget (looks like bg_<id>.<secret>). Pass it to every sub-agent so they all spend from the same budget."
         },
         "usd": {
          "type": "number",
          "description": "Estimated cost of the next call in USD."
         },
         "tokens": {
          "type": "integer",
          "description": "Estimated tokens of the next call."
         },
         "calls": {
          "type": "integer",
          "description": "Number of calls to reserve (default 1)."
         },
         "note": {
          "type": "string",
          "description": "Short note, e.g. \"web_search #12\"."
         }
        },
        "required": [
         "handle"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/record": {
   "post": {
    "operationId": "budget_guard__record",
    "tags": [
     "budget-guard"
    ],
    "summary": "record",
    "description": "After a call, report the actual usage minus what you reserved (positive if it cost more, negative to give back an overestimate). Keeps the ledger honest without blocking.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "handle": {
          "type": "string",
          "description": "The budget handle from open_budget (looks like bg_<id>.<secret>). Pass it to every sub-agent so they all spend from the same budget."
         },
         "usd": {
          "type": "number"
         },
         "tokens": {
          "type": "integer"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "handle"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/weekly_report": {
   "post": {
    "operationId": "budget_guard__weekly_report",
    "tags": [
     "budget-guard"
    ],
    "summary": "weekly report",
    "description": "Draft a plain-English report of the last 7 days for your key (or IP): runs, spend, runs that went over their cap, and dollars spent after a cap would have stopped them. Read-only; sends nothing. Show it to your user.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/budget_status": {
   "post": {
    "operationId": "budget_guard__budget_status",
    "tags": [
     "budget-guard"
    ],
    "summary": "budget status",
    "description": "See spend vs limits, remaining budget, whether the run is stopped and why, and the last 20 ledger events. Set close=true when the run finishes.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "handle": {
          "type": "string",
          "description": "The budget handle from open_budget (looks like bg_<id>.<secret>). Pass it to every sub-agent so they all spend from the same budget."
         },
         "close": {
          "type": "boolean"
         }
        },
        "required": [
         "handle"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/budget-guard/usage_and_upgrade": {
   "post": {
    "operationId": "budget_guard__usage_and_upgrade",
    "tags": [
     "budget-guard"
    ],
    "summary": "usage and upgrade",
    "description": "Show today's usage for your key or IP and the $49/month All Access upgrade link (cloudforbots.com/offer). Free tier: 500 calls/day. Not metered.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/check_action": {
   "post": {
    "operationId": "approval_rules__check_action",
    "tags": [
     "approval-rules"
    ],
    "summary": "check action",
    "description": "Before a consequential action, ask whether the user's rules auto-approve it. Returns auto_approve, needs_approval (with a plain-English approval card and a link the user can tap) or deny. Free plan is watch-only: it reports what it WOULD decide and logs it for the weekly report, but nothing counts as approved, so ask your user as you normally would.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "action": {
          "type": "object",
          "description": "The action your agent wants to take.",
          "properties": {
           "type": {
            "type": "string",
            "description": "Short verb: read, search, draft, send_email, send_message, post, purchase, payment, delete, deploy, change_settings, ..."
           },
           "summary": {
            "type": "string",
            "description": "One plain sentence of what will happen, e.g. \"Buy a $3.99 domain renewal for example.com\"."
           },
           "amount_usd": {
            "type": "number",
            "description": "Money involved, if any."
           },
           "target": {
            "type": "string",
            "description": "Who or what it touches: a recipient email, domain, file, repo."
           },
           "reversible": {
            "type": "boolean",
            "description": "Can it be undone easily?"
           }
          },
          "required": [
           "type",
           "summary"
          ]
         }
        },
        "required": [
         "action"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/get_decision": {
   "post": {
    "operationId": "approval_rules__get_decision",
    "tags": [
     "approval-rules"
    ],
    "summary": "get decision",
    "description": "Check whether the user approved or rejected an approval card (by card_id and card_token). Only act when status is approved.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "card_id": {
          "type": "string"
         },
         "card_token": {
          "type": "string"
         }
        },
        "required": [
         "card_id",
         "card_token"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/set_rules": {
   "post": {
    "operationId": "approval_rules__set_rules",
    "tags": [
     "approval-rules"
    ],
    "summary": "set rules",
    "description": "Save the user's rules (needs an API key so they stay yours). Each rule: {name, when:{type:[...], max_usd, min_usd, reversible, target_ends_with:[...]}, verdict:\"auto\"|\"ask\"|\"deny\"}. First matching rule wins; anything unmatched needs approval. Call once without confirm to preview, then with confirm=true.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "rules": {
          "type": "array",
          "items": {
           "type": "object"
          }
         },
         "confirm": {
          "type": "boolean"
         }
        },
        "required": [
         "rules"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/get_rules": {
   "post": {
    "operationId": "approval_rules__get_rules",
    "tags": [
     "approval-rules"
    ],
    "summary": "get rules",
    "description": "Show the rules in effect for your key (or the default starter rules).",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/weekly_report": {
   "post": {
    "operationId": "approval_rules__weekly_report",
    "tags": [
     "approval-rules"
    ],
    "summary": "weekly report",
    "description": "Draft a plain-English report of the last 7 days: actions checked, how many your rules would auto-approve, how many needed you, top kinds of asks, and rule suggestions. Read-only; sends nothing.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/approval-rules/usage_and_upgrade": {
   "post": {
    "operationId": "approval_rules__usage_and_upgrade",
    "tags": [
     "approval-rules"
    ],
    "summary": "usage and upgrade",
    "description": "Show today's usage for your key or IP and the $49/month All Access upgrade link (cloudforbots.com/offer). Free tier: 200 calls/day. Not metered.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/agent-wake/schedule_wake": {
   "post": {
    "operationId": "agent_wake__schedule_wake",
    "tags": [
     "agent-wake"
    ],
    "summary": "schedule wake",
    "description": "Schedule a wake-up for your agent. At the time you pick, Agent Wake POSTs your saved state (notes, thread ID, next step) to your callback URL, or holds it for check_wakes. Use it so a later, fresh session picks up where this one stopped. Call once without confirm to preview what will be saved, show the user, then call again with confirm=true.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "when": {
          "type": "string",
          "description": "When to wake: \"in 30 minutes\", \"in 2 hours\", \"in 3 days\", or an ISO time like 2026-10-16T14:00:00Z. Between 1 minute and 365 days out."
         },
         "repeat_minutes": {
          "type": "integer",
          "description": "Optional. Repeat every N minutes after the first wake (free: 60+, Pro: 5+)."
         },
         "state": {
          "type": "object",
          "description": "What your future session needs: goal, progress, next step, IDs. Free up to 8 KB, Pro up to 64 KB."
         },
         "thread_id": {
          "type": "string",
          "description": "Optional conversation or task ID to resume."
         },
         "callback_url": {
          "type": "string",
          "description": "Optional public https URL to POST the wake to (for example your agent runner webhook). Without it, collect wakes with check_wakes."
         },
         "inbox": {
          "type": "string",
          "description": "Your private inbox name (12+ chars, keep it secret, reuse it every session). Optional if you send an API key: then the key is your inbox."
         },
         "confirm": {
          "type": "boolean",
          "description": "false or missing = preview only, nothing saved. true = save the wake."
         }
        },
        "required": [
         "when"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/agent-wake/check_wakes": {
   "post": {
    "operationId": "agent_wake__check_wakes",
    "tags": [
     "agent-wake"
    ],
    "summary": "check wakes",
    "description": "Collect wake-ups that are due for your inbox (each returned once, with its saved state and thread ID) and list upcoming ones. Call this at the start of a session.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "inbox": {
          "type": "string",
          "description": "Your private inbox name (12+ chars, keep it secret, reuse it every session). Optional if you send an API key: then the key is your inbox."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/agent-wake/cancel_wake": {
   "post": {
    "operationId": "agent_wake__cancel_wake",
    "tags": [
     "agent-wake"
    ],
    "summary": "cancel wake",
    "description": "Cancel a pending wake-up by wake_id. Call once without confirm to preview, then with confirm=true.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "wake_id": {
          "type": "string"
         },
         "inbox": {
          "type": "string",
          "description": "Your private inbox name (12+ chars, keep it secret, reuse it every session). Optional if you send an API key: then the key is your inbox."
         },
         "confirm": {
          "type": "boolean"
         }
        },
        "required": [
         "wake_id"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/agent-wake/usage_and_upgrade": {
   "post": {
    "operationId": "agent_wake__usage_and_upgrade",
    "tags": [
     "agent-wake"
    ],
    "summary": "usage and upgrade",
    "description": "Show today's usage for your key or IP and the $49/month All Access upgrade link. Free tier: 50 calls/day. Not metered.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string",
       "maxLength": 128
      },
      "description": "Makes the call safe to retry; the same key returns the stored reply for 24h."
     },
     {
      "name": "X-Request-Id",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Tool result (always includes request_id).",
      "headers": {
       "x-request-id": {
        "schema": {
         "type": "string"
        }
       },
       "x-ratelimit-remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad JSON",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or bad key",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Key limits (scopes) do not allow this tool or action",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "const": false
          },
          "error": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "message": {
             "type": "string"
            }
           }
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Same Idempotency-Key still running"
     },
     "422": {
      "description": "Idempotency-Key reused on another route"
     },
     "429": {
      "description": "Over 20 requests/second for this key, or free daily limit",
      "headers": {
       "retry-after": {
        "schema": {
         "type": "integer"
        }
       }
      }
     },
     "504": {
      "description": "Tool did not answer in time; retry with the same Idempotency-Key"
     }
    }
   }
  },
  "/api/v1/secrets": {
   "post": {
    "operationId": "secrets__call",
    "tags": [
     "secrets"
    ],
    "summary": "Store API keys once; values are encrypted and never returned. Actions: set (name, value, allowed_hosts), list, delete. Free: 25 secrets.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "set",
           "list",
           "delete"
          ]
         },
         "name": {
          "type": "string",
          "pattern": "^[A-Z][A-Z0-9_]{0,63}$"
         },
         "value": {
          "type": "string"
         },
         "allowed_hosts": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/fetch": {
   "post": {
    "operationId": "fetch__call",
    "tags": [
     "secrets"
    ],
    "summary": "Secrets proxy: call an https API with {{secret:NAME}} filled in server-side, only to allowed hosts, no private networks, no redirects; secret values redacted from the reply.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "url"
        ],
        "properties": {
         "url": {
          "type": "string"
         },
         "method": {
          "type": "string"
         },
         "headers": {
          "type": "object"
         },
         "body": {},
         "timeout_s": {
          "type": "integer",
          "maximum": 30
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "upstream status, headers and body (redacted)"
     },
     "403": {
      "description": "host not allowed or private address"
     }
    }
   }
  },
  "/api/v1/queue": {
   "post": {
    "operationId": "queue__call",
    "tags": [
     "queue"
    ],
    "summary": "Durable queues: push, pull (visibility timeout), ack, nack, stats, dead, retry_dead. At-least-once with dead letters. Free: 1,000 queues, 10,000 pushes/day.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "push",
           "pull",
           "ack",
           "nack",
           "stats",
           "dead",
           "retry_dead",
           "purge"
          ]
         },
         "queue": {
          "type": "string"
         },
         "message": {},
         "messages": {
          "type": "array"
         },
         "delay_s": {
          "type": "integer"
         },
         "max_attempts": {
          "type": "integer"
         },
         "max": {
          "type": "integer",
          "maximum": 10
         },
         "visibility_s": {
          "type": "integer"
         },
         "ids": {
          "type": "array",
          "items": {
           "type": "integer"
          }
         },
         "error": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     },
     "429": {
      "description": "free limit reached"
     }
    }
   }
  },
  "/api/v1/cron": {
   "post": {
    "operationId": "cron__call",
    "tags": [
     "cron"
    ],
    "summary": "Schedules (5-field cron, UTC) that call an https URL through the secrets proxy or push to a queue. Actions: set, list, history, pause, resume, delete, run_now. Also one-off timers. Free: 1,000 schedules + timers.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "set",
           "timer",
           "list",
           "history",
           "pause",
           "resume",
           "delete",
           "run_now"
          ]
         },
         "name": {
          "type": "string"
         },
         "schedule": {
          "type": "string"
         },
         "request": {
          "type": "object"
         },
         "at": {
          "type": "string",
          "format": "date-time"
         },
         "delay_s": {
          "type": "integer"
         },
         "webhook": {
          "type": "string"
         },
         "queue": {
          "type": "string"
         },
         "payload": {}
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/browser": {
   "post": {
    "operationId": "browser__call",
    "tags": [
     "browser"
    ],
    "summary": "Headless Chrome sessions that pause instead of dying (cookies and logins survive). Actions: open, run (steps), screenshot, pause, kill, list. fill values can use {{secret:NAME}}.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "open",
           "run",
           "screenshot",
           "pause",
           "kill",
           "list",
           "handoff"
          ]
         },
         "session": {
          "type": "string"
         },
         "kind": {
          "type": "string"
         },
         "note": {
          "type": "string"
         },
         "fill": {
          "type": "string"
         },
         "submit": {
          "type": "boolean"
         },
         "wait_s": {
          "type": "integer"
         },
         "url": {
          "type": "string"
         },
         "steps": {
          "type": "array",
          "items": {
           "type": "object"
          }
         },
         "screenshot": {
          "type": "boolean"
         },
         "text": {
          "type": "boolean"
         },
         "links": {
          "type": "boolean"
         },
         "max_text": {
          "type": "integer"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "url, title, text, links, step results"
     },
     "422": {
      "description": "a step failed"
     }
    }
   }
  },
  "/api/v1/lock": {
   "post": {
    "operationId": "lock__call",
    "tags": [
     "coordination"
    ],
    "summary": "Named locks/leases shared across your bots: acquire (ttl_s, wait_s), renew, release, status, list. Auto-release on expiry; fencing token.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "acquire",
           "renew",
           "release",
           "status",
           "list"
          ]
         },
         "name": {
          "type": "string"
         },
         "holder": {
          "type": "string"
         },
         "ttl_s": {
          "type": "integer"
         },
         "wait_s": {
          "type": "integer",
          "maximum": 25
         },
         "token": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/rate": {
   "post": {
    "operationId": "rate__call",
    "tags": [
     "coordination"
    ],
    "summary": "Shared sliding-window rate counters (\"6/6h\"): take, peek, list.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "take",
           "peek",
           "list"
          ]
         },
         "name": {
          "type": "string"
         },
         "limit": {
          "type": "string",
          "example": "6/6h"
         },
         "n": {
          "type": "integer"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "allowed, remaining, retry_after_s"
     }
    }
   }
  },
  "/api/v1/inbox": {
   "post": {
    "operationId": "inbox__call",
    "tags": [
     "bus"
    ],
    "summary": "Per-bot public webhook URL feeding queue inbox.<name>: create (rotates), list, delete.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "create",
           "list",
           "delete"
          ]
         },
         "name": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok (create returns url)"
     }
    }
   }
  },
  "/api/v1/topic": {
   "post": {
    "operationId": "topic__call",
    "tags": [
     "bus"
    ],
    "summary": "Durable topics: subscribe queues, publish fans out to each; consumers pull + ack via /api/v1/queue.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "subscribe",
           "unsubscribe",
           "publish",
           "list"
          ]
         },
         "topic": {
          "type": "string"
         },
         "queue": {
          "type": "string"
         },
         "message": {},
         "messages": {
          "type": "array"
         },
         "from": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/ledger": {
   "post": {
    "operationId": "ledger__call",
    "tags": [
     "ledger"
    ],
    "summary": "Run ledger across bots: record runs, one feed (runs + costs + failed API calls), daily rollup per bot.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "record",
           "feed",
           "rollup"
          ]
         },
         "bot": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "ok",
           "failed",
           "running",
           "skipped"
          ]
         },
         "run_id": {
          "type": "string"
         },
         "kind": {
          "type": "string"
         },
         "usd": {
          "type": "number"
         },
         "tokens_in": {
          "type": "integer"
         },
         "tokens_out": {
          "type": "integer"
         },
         "model": {
          "type": "string"
         },
         "ms": {
          "type": "integer"
         },
         "note": {
          "type": "string"
         },
         "since": {
          "type": "string"
         },
         "limit": {
          "type": "integer"
         },
         "days": {
          "type": "integer"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/sandbox": {
   "post": {
    "operationId": "sandbox__call",
    "tags": [
     "sandbox"
    ],
    "summary": "Named Linux sandbox that pauses instead of dying (files, packages and running processes survive). Actions: run, write_file, read_file, sync_from_store, sync_to_store, status, pause, kill, list. Auto-pauses after 1 idle minute. Free: 3 sandboxes, 60 run-minutes/day.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string",
          "enum": [
           "run",
           "write_file",
           "read_file",
           "sync_from_store",
           "sync_to_store",
           "status",
           "pause",
           "kill",
           "list"
          ]
         },
         "name": {
          "type": "string"
         },
         "ns": {
          "type": "string"
         },
         "prefix": {
          "type": "string"
         },
         "dir": {
          "type": "string"
         },
         "secrets": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "run: secret names usable from code via $CFB_FETCH_URL + $CFB_FETCH_TOKEN (15 min); values never enter the sandbox"
         },
         "cmd": {
          "type": "string"
         },
         "cwd": {
          "type": "string"
         },
         "timeout_s": {
          "type": "integer",
          "maximum": 120
         },
         "background": {
          "type": "boolean"
         },
         "path": {
          "type": "string"
         },
         "content": {
          "type": "string"
         },
         "content_base64": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     },
     "429": {
      "description": "free limit reached"
     }
    }
   }
  },
  "/api/v1/store": {
   "post": {
    "operationId": "store__call",
    "tags": [
     "storage"
    ],
    "summary": "Bot storage (KV + files). Actions: get, set, incr, delete, list, file_put, file_get, file_meta, file_delete, file_list, usage. Free: 100 MB, 100k items, values up to 256 KB, files up to 4 MB.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "action"
        ],
        "properties": {
         "action": {
          "type": "string"
         },
         "ns": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "value": {},
         "ttl_seconds": {
          "type": "integer"
         },
         "if_version": {
          "type": "integer"
         },
         "by": {
          "type": "number"
         },
         "prefix": {
          "type": "string"
         },
         "after": {
          "type": "string"
         },
         "limit": {
          "type": "integer"
         },
         "name": {
          "type": "string"
         },
         "content_base64": {
          "type": "string"
         },
         "content_type": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     },
     "409": {
      "description": "version_conflict"
     },
     "507": {
      "description": "storage_full"
     }
    }
   }
  },
  "/api/v1/store/kv/{ns}/{key}": {
   "get": {
    "operationId": "store__kv_get",
    "tags": [
     "storage"
    ],
    "summary": "Read a value (404 if missing)",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "key",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   },
   "put": {
    "operationId": "store__kv_put",
    "tags": [
     "storage"
    ],
    "summary": "Write a JSON value. ?ttl_seconds= to expire; If-Match: <version> for compare-and-set",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "key",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {}
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   },
   "delete": {
    "operationId": "store__kv_delete",
    "tags": [
     "storage"
    ],
    "summary": "Delete a value",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "key",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/store/files/{ns}/{name}": {
   "get": {
    "operationId": "store__file_download",
    "tags": [
     "storage"
    ],
    "summary": "Download a file (raw bytes)",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "file"
     }
    }
   },
   "put": {
    "operationId": "store__file_upload",
    "tags": [
     "storage"
    ],
    "summary": "Upload a file (raw body, up to 4 MB; Content-Type is kept)",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   },
   "delete": {
    "operationId": "store__file_delete",
    "tags": [
     "storage"
    ],
    "summary": "Delete a file",
    "parameters": [
     {
      "name": "ns",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/webhooks/deliveries": {
   "get": {
    "operationId": "webhooks__deliveries",
    "tags": [
     "webhooks"
    ],
    "summary": "Delivery log: last 50 deliveries with status, tries and response code.",
    "parameters": [
     {
      "name": "filter",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "failed, pending, delivered or an event name"
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/webhooks/redeliver/{id}": {
   "post": {
    "operationId": "webhooks__redeliver",
    "tags": [
     "webhooks"
    ],
    "summary": "Send a past delivery again (same payload, fresh signature, new delivery id). Up to 60 an hour.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/webhooks/rotate": {
   "post": {
    "operationId": "webhooks__rotate",
    "tags": [
     "webhooks"
    ],
    "summary": "New signing secret for an endpoint.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "url"
        ],
        "properties": {
         "url": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "description": "The old secret stays valid 24h; during that window cfb-signature carries two v1= values, accept either.",
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/webhooks": {
   "get": {
    "operationId": "webhooks__list",
    "tags": [
     "webhooks"
    ],
    "summary": "List webhook endpoints and recent deliveries",
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   },
   "post": {
    "operationId": "webhooks__add",
    "tags": [
     "webhooks"
    ],
    "summary": "Add an endpoint (returns the signing secret once). Add ?test=1 to send a signed webhook.test event instead.",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "url"
        ],
        "properties": {
         "url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://"
         },
         "events": {
          "type": "array",
          "items": {
           "type": "string",
           "enum": [
            "*",
            "loop.halted",
            "budget.stopped",
            "approval.held",
            "call.failed",
            "webhook.test"
           ]
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   },
   "delete": {
    "operationId": "webhooks__remove",
    "tags": [
     "webhooks"
    ],
    "summary": "Remove an endpoint",
    "parameters": [
     {
      "name": "url",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/api/v1/audit": {
   "get": {
    "operationId": "audit__list",
    "tags": [
     "audit"
    ],
    "summary": "Audit log of every v1/MCP call on your account, newest first",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 200,
       "default": 50
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  }
 }
}