{
  "openapi": "3.1.0",
  "info": {
    "title": "MicroSaaS Platform Control API",
    "version": "v1",
    "description": "Whitelisted methods on the control plane, callable by any authenticated token or session. See /docs/llms.txt for the agent-facing manual."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "tags": [
    {
      "name": "Micro SaaS",
      "description": "Whitelisted methods in `platform_control.api.micro_saas`."
    },
    {
      "name": "Credentials",
      "description": "Whitelisted methods in `platform_control.credentials`."
    },
    {
      "name": "Site Lifecycle",
      "description": "Whitelisted methods in `platform_control.api.lifecycle`."
    },
    {
      "name": "Usage",
      "description": "Whitelisted methods in `platform_control.api.usage`."
    },
    {
      "name": "Marketplace",
      "description": "Whitelisted methods in `platform_control.api.marketplace`."
    },
    {
      "name": "API Credentials",
      "description": "Whitelisted methods in `platform_control.api.credentials`."
    },
    {
      "name": "Domains",
      "description": "Whitelisted methods in `platform_control.api.domains`."
    },
    {
      "name": "Deployments",
      "description": "Whitelisted methods in `platform_control.api.deployments`."
    },
    {
      "name": "Team",
      "description": "Whitelisted methods in `platform_control.api.team`."
    },
    {
      "name": "Sessions",
      "description": "Whitelisted methods in `platform_control.api.sessions`."
    },
    {
      "name": "Approvals",
      "description": "Whitelisted methods in `platform_control.api.approvals`."
    }
  ],
  "paths": {
    "/api/method/platform_control.api.micro_saas.create_micro_saas": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "Create a Micro SaaS row and kick off async provisioning.",
        "description": "Create a Micro SaaS row and kick off async provisioning.\n\nReturns `{\"micro_saas\": <name>, \"provisioning_job\": <job_name>, \"status\":\n\"Provisioning\"}`. Provisioning itself runs synchronously in this same\nrequest today (V1's LocalBenchBackend is not async); the return shape\nmatches what the P0 AgentBackend will return once provisioning goes\nbackground, so callers do not change.\n\nValidation invariants mirror those on the doctype (`micro_saas.py`):\n  - slug matches the DNS-safe pattern\n  - slug is not reserved\n  - account exists (and the caller has permission on it, via the\n    `frappe.get_doc(..., check_permission=True)` guard)\n  - slug is globally unique -- return the typed envelope, not the\n    default Frappe DuplicateEntryError",
        "operationId": "micro_saas__create_micro_saas",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "end_user_type": {
                    "type": "string"
                  },
                  "isolation_tier": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string"
                  }
                },
                "required": [
                  "account",
                  "slug"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.micro_saas.get_micro_saas": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "Fetch a single Micro SaaS row (permission-checked via get_doc).",
        "description": "Fetch a single Micro SaaS row (permission-checked via get_doc).",
        "operationId": "micro_saas__get_micro_saas",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.micro_saas.get_owner_login_info": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "Return the tenant Owner login details for the wizard's success step.",
        "description": "Return the tenant Owner login details for the wizard's success step.\n\nThe owner logs into the tenant with the SAME email + password they use\non the control plane \u2014 provisioning mirrors the control User's password\nhash onto the tenant `__Auth` row (see `provisioning.provision.\n_resolve_owner_identity` + `LocalBenchBackend.provision_owner_user`).\nSo this method never returns a password \u2014 the owner already knows\nit. Returns:\n\n  {\n    \"owner_email\":       \"ada@example.com\",\n    \"owner_name\":        \"Ada\",\n    \"desk_hostname\":     \"<slug>.<base-domain>\",\n    \"app_hostname\":      \"<slug>-app.<base-domain>\",\n    \"site_name\":         \"<slug>.<base-domain>\",\n    \"note\":              \"Use the same password you signed up with.\",\n  }\n\nRefuses non-Active sites with a typed envelope so the wizard can\nrender a clear \"still provisioning\" state rather than showing empty\nfields.",
        "operationId": "micro_saas__get_owner_login_info",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.micro_saas.get_provisioning_status": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "Return the current provisioning state of a Micro SaaS -- the shape",
        "description": "Return the current provisioning state of a Micro SaaS -- the shape\nthe Golden Path polls between `create` and `credentials.reveal`.\n\nIDM-3 addition: the returned `provisioning_job` now includes a\nparsed `steps` list (list of {step, status, started, finished, log}\ndicts) so the wizard can render real per-step progress instead of a\nfake spinner cascade.",
        "operationId": "micro_saas__get_provisioning_status",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.micro_saas.list_micro_saas": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "List Micro SaaS rows visible to the caller (Layer-2 scoping applies).",
        "description": "List Micro SaaS rows visible to the caller (Layer-2 scoping applies).\n\nOptional `account` filter to narrow to one account -- the caller still\nonly sees rows they have permission on, so the filter is a hint, not a\ngrant.",
        "operationId": "micro_saas__list_micro_saas",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.micro_saas.retry_provisioning": {
      "post": {
        "tags": [
          "Micro SaaS"
        ],
        "summary": "Re-kick provisioning for a Micro SaaS row that is Failed or stuck in",
        "description": "Re-kick provisioning for a Micro SaaS row that is Failed or stuck in\nProvisioning without a job. Fires the same seam the `after_insert`\nhook does; the underlying `provision_micro_saas` has a preflight\ncheck that refuses if the tenant site already exists on disk, so an\naccidental double-run against an already-provisioned slug fails\ncleanly rather than corrupting state.\n\nRequires `write` permission on the Micro SaaS.",
        "operationId": "micro_saas__retry_provisioning",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.credentials.reveal": {
      "post": {
        "tags": [
          "Credentials"
        ],
        "summary": "One-time reveal for a Micro SaaS's admin credentials.",
        "description": "One-time reveal for a Micro SaaS's admin credentials.\n\nReturns `{site_name, admin_user, admin_password, hostnames, ...}`\nexactly once. Subsequent calls return `409 credentials_already_revealed`\nand the caller must rotate to see a password again -- that is the whole\npoint of the reveal being one-time (design doc DoD 4).\n\nRaises `PermissionError` when the caller lacks write on the Micro SaaS.",
        "operationId": "credentials__reveal",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.credentials.rotate": {
      "post": {
        "tags": [
          "Credentials"
        ],
        "summary": "Generate a fresh admin password for a Micro SaaS.",
        "description": "Generate a fresh admin password for a Micro SaaS.\n\nWrites the new password to the site (via the LocalBenchBackend's\n`set_admin_password` -- wraps `bench --site X set-admin-password`),\nupdates the encrypted `Password` field on the doc, resets\n`credentials_revealed_at`, and marks `credentials_rotated_at`. Returns\nthe reveal payload directly -- rotation implicitly reveals, because the\nonly reason to rotate is to get a fresh password back.\n\nRotation is T3 (design doc \u00a721 P1). Every attempt (successful or not)\nis audited.",
        "operationId": "credentials__rotate",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.archive": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Soft suspend \u2014 site stays on disk, requests rejected. Reversible.",
        "description": "Soft suspend \u2014 site stays on disk, requests rejected. Reversible.",
        "operationId": "lifecycle__archive",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.delete": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Hard delete: take a pre_delete backup, drop the site, keep the row.",
        "description": "Hard delete: take a pre_delete backup, drop the site, keep the row.\n\nRequires `confirm=<slug>` to guard against misclick. The tenant row\nis retained (status=Deleted, deleted_at stamped) so a Restore can\nrecreate it under the same slug.\n\nT4: a token-authenticated caller additionally needs an Approved\n`approval_ok` (action \"lifecycle.delete\") \u2014 an autonomous agent must\nnot destroy a production site on its own say-so.",
        "operationId": "lifecycle__delete",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "confirm": {
                    "type": "string"
                  },
                  "approval_ok": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.lifecycle_summary": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "One-shot summary for the Micro SaaS form UI: current lifecycle",
        "description": "One-shot summary for the Micro SaaS form UI: current lifecycle\nstate + latest backup + latest restore job.",
        "operationId": "lifecycle__lifecycle_summary",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.list_backups": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Public backup list \u2014 returns just filenames (`database_filename`",
        "description": "Public backup list \u2014 returns just filenames (`database_filename`\netc), never full server paths, so the UI + agent see the same\nminimal representation. Restore internals load the row by name +\nread the hidden path field server-side.",
        "operationId": "lifecycle__list_backups",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.restore": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Restore a Micro SaaS from a Site Backup.",
        "description": "Restore a Micro SaaS from a Site Backup.\n\nTwo modes:\n  * `name` is Deleted: recreate the site (bench new-site with a\n    temporary admin password), then bench-restore the DB.\n  * `name` is Active/Archived/Suspended/Failed: take a pre_restore\n    safety backup, then bench-restore the DB in-place.\n\nDefaults to the most recent successful `pre_delete` (for deleted\nsites) or `manual` (for live sites) backup when `backup` is omitted.\n\nT3: a token-authenticated caller needs an Approved `approval_ok`\n(action \"lifecycle.restore\") \u2014 an in-place restore overwrites the\nlive DB.",
        "operationId": "lifecycle__restore",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "backup": {
                    "type": "string"
                  },
                  "approval_ok": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.take_backup": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Take a full backup of a live tenant site. Returns the Site Backup name.",
        "description": "Take a full backup of a live tenant site. Returns the Site Backup name.\n\nKind: manual | scheduled | pre_delete | pre_restore. Non-owners with\nwrite permission (via User Permission \u2192 Account) may call this.",
        "operationId": "lifecycle__take_backup",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "kind": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.lifecycle.unarchive": {
      "post": {
        "tags": [
          "Site Lifecycle"
        ],
        "summary": "Unarchive",
        "description": "",
        "operationId": "lifecycle__unarchive",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.usage.list_usage": {
      "post": {
        "tags": [
          "Usage"
        ],
        "summary": "List Usage",
        "description": "",
        "operationId": "usage__list_usage",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "metric": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.apps_manifest": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Return the tenant's apps.json \u2014 used by the SPA + agents to see what's installed.",
        "description": "Return the tenant's apps.json \u2014 used by the SPA + agents to see what's installed.",
        "operationId": "marketplace__apps_manifest",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.check_compatibility": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Dry-run: full collision + dependency check. Never mutates.",
        "description": "Dry-run: full collision + dependency check. Never mutates.",
        "operationId": "marketplace__check_compatibility",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "item": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  },
                  "micro_saas": {
                    "type": "string"
                  }
                },
                "required": [
                  "item",
                  "version",
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.get_item": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Get Item",
        "description": "",
        "operationId": "marketplace__get_item",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string"
                  }
                },
                "required": [
                  "slug"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.install": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Install a Marketplace Item version onto a Micro SaaS site.",
        "description": "Install a Marketplace Item version onto a Micro SaaS site.",
        "operationId": "marketplace__install",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "item": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  },
                  "micro_saas": {
                    "type": "string"
                  }
                },
                "required": [
                  "item",
                  "version",
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.lint_manifest": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Preflight-validate a manifest's shape (\u00a713). Agent-facing alias for",
        "description": "Preflight-validate a manifest's shape (\u00a713). Agent-facing alias for\n`platform_control.marketplace_lint.lint_manifest` so it sits in the same\n`api.marketplace.*` namespace agents already use. Returns\n`{ok, errors[], warnings[]}`. No tenant + no side effects.",
        "operationId": "marketplace__lint_manifest",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "manifest": {
                    "type": "string"
                  }
                },
                "required": [
                  "manifest"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.list_installs": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "List Installs",
        "description": "",
        "operationId": "marketplace__list_installs",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.list_items": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "List Items",
        "description": "",
        "operationId": "marketplace__list_items",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string"
                  },
                  "kind": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.scan_version": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Static safety scan of a version's bundle (PRODUCTION_READINESS \u00a74). Review",
        "description": "Static safety scan of a version's bundle (PRODUCTION_READINESS \u00a74). Review\nsurface for platform staff: `{ok, critical_count, findings[]}`. The same scan\nruns as a hard gate inside `install()`, so authors still see why a bundle was\nrejected without this endpoint. Staff-gated because it reads + decompresses an\narbitrary uploaded bundle \u2014 an open endpoint is a cheap DoS lever (F9).",
        "operationId": "marketplace__scan_version",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "item": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  }
                },
                "required": [
                  "item",
                  "version"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.uninstall": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Reverse an install using the saved inventory.",
        "description": "Reverse an install using the saved inventory.",
        "operationId": "marketplace__uninstall",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "install": {
                    "type": "string"
                  },
                  "force": {
                    "type": "integer"
                  }
                },
                "required": [
                  "install"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.upgrade": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Upgrade an installed app to a newer version IN PLACE, preserving tenant",
        "description": "Upgrade an installed app to a newer version IN PLACE, preserving tenant\ndata (PRODUCTION_READINESS \u00a75). Additively applies the new version's schema\n(new DocTypes/fields/roles \u2014 never a drop) and swaps the frontend bundle; a\npre-upgrade snapshot + DB rollback make it atomic. Owner-scoped via the\ninstall's Micro SaaS.",
        "operationId": "marketplace__upgrade",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "install": {
                    "type": "string"
                  },
                  "to_version": {
                    "type": "string"
                  }
                },
                "required": [
                  "install",
                  "to_version"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.marketplace.yank_version": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Yank a version (platform staff only) \u2014 takedown of a bad/compromised",
        "description": "Yank a version (platform staff only) \u2014 takedown of a bad/compromised\nrelease (PRODUCTION_READINESS \u00a74). Sets status=Yanked, so `install()` (which\nonly accepts Approved) refuses it from now on; already-installed tenants keep\nrunning but their installs are flagged for operator review. Reversible by an\noperator flipping status back to Approved.",
        "operationId": "marketplace__yank_version",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "item": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "required": [
                  "item",
                  "version"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.credentials.issue": {
      "post": {
        "tags": [
          "API Credentials"
        ],
        "summary": "Issue a new API Credential.",
        "description": "Issue a new API Credential.\n\nReturns `{api_credential, api_key, api_secret, scope_preset, expires_at}`.\nThis is the ONLY time `api_secret` appears in a response for this row;\nsubsequent reads via `get_credential` never include it. Callers who\nlose the secret must rotate.\n\nRequires write permission on the Micro SaaS (or Platform * role if\n`micro_saas` is blank, for a platform-scoped token).",
        "operationId": "credentials__issue",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "micro_saas": {
                    "type": "string"
                  },
                  "scope_preset": {
                    "type": "string"
                  },
                  "expires_at": {
                    "type": "string"
                  }
                },
                "required": [
                  "label"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.credentials.list_credentials": {
      "post": {
        "tags": [
          "API Credentials"
        ],
        "summary": "List API Credentials visible to the caller. Never includes the secret.",
        "description": "List API Credentials visible to the caller. Never includes the secret.",
        "operationId": "credentials__list_credentials",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.credentials.reveal": {
      "post": {
        "tags": [
          "API Credentials"
        ],
        "summary": "One-time reveal of an API Credential's secret.",
        "description": "One-time reveal of an API Credential's secret.\n\nSame pattern as `platform_control.credentials.reveal` for admin\npasswords: succeeds only if `secret_revealed_at` is empty (or the\ncredential was just rotated). Otherwise returns\n`409 api_secret_already_revealed`.\n\nT3: a token-authenticated caller must present an Approved\n`approval_ok` (action \"credentials.reveal\"); interactive sessions and\nstaff pass. See approvals.require_agent_approval.",
        "operationId": "credentials__reveal",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "approval_ok": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.credentials.revoke": {
      "post": {
        "tags": [
          "API Credentials"
        ],
        "summary": "Revoke a credential \u2014 sets status=Revoked and stamps revoked_at.",
        "description": "Revoke a credential \u2014 sets status=Revoked and stamps revoked_at.\n\nRatcheting: once revoked, the row can never go back to Active (a new\ncredential is a new row). \u00a76.4's kill switch depends on this being\nirreversible.",
        "operationId": "credentials__revoke",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.credentials.rotate": {
      "post": {
        "tags": [
          "API Credentials"
        ],
        "summary": "Mint a new api_secret for an existing credential. Returns the reveal",
        "description": "Mint a new api_secret for an existing credential. Returns the reveal\npayload directly (rotation implicitly reveals \u2014 same shape as admin\npassword rotation).\n\nT3: token-authenticated callers need an Approved `approval_ok`\n(action \"credentials.rotate\").",
        "operationId": "credentials__rotate",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "approval_ok": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.add_custom_domain": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Attach a domain to a Micro SaaS. Returns the TXT record the caller",
        "description": "Attach a domain to a Micro SaaS. Returns the TXT record the caller\nmust publish before the domain can be verified.\n\nRequires write permission on the Micro SaaS.",
        "operationId": "domains__add_custom_domain",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "is_primary": {
                    "type": "integer"
                  }
                },
                "required": [
                  "micro_saas",
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.add_email_sending_domain": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Attach an email sending domain. Returns the DKIM/SPF/DMARC records",
        "description": "Attach an email sending domain. Returns the DKIM/SPF/DMARC records\nthe caller must publish.",
        "operationId": "domains__add_email_sending_domain",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "micro_saas",
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.get_domain_status": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Get Domain Status",
        "description": "",
        "operationId": "domains__get_domain_status",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.get_email_domain_status": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Get Email Domain Status",
        "description": "",
        "operationId": "domains__get_email_domain_status",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.remove_custom_domain": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "T3 removal \u2014 dual audit + immediate delete. Token-authenticated",
        "description": "T3 removal \u2014 dual audit + immediate delete. Token-authenticated\ncallers need an Approved `approval_ok` (action \"domains.remove\").",
        "operationId": "domains__remove_custom_domain",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "approval_ok": {
                    "type": "string"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.domains.verify_custom_domain": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Attempt to verify the domain (TXT lookup stub).",
        "description": "Attempt to verify the domain (TXT lookup stub).\n\nV1: since we have no DNS resolver seam in the control-plane process,\nthis endpoint returns the current status and stamps `verified_at` if\na `platform_control.flags.domain_verify_ok` flag is set (dev/test\noverride). Production wires in a real resolver call.",
        "operationId": "domains__verify_custom_domain",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.deployments.create_deployment": {
      "post": {
        "tags": [
          "Deployments"
        ],
        "summary": "Register a new deployment. `version_key` is auto-generated as",
        "description": "Register a new deployment. `version_key` is auto-generated as\n`<micro_saas>-<hash>` so it's unique per Micro SaaS per upload.\n\nRequires write permission on the Micro SaaS. V1 marks the row Ready\nimmediately (there's no real upload progress to observe); production\nstarts it in Uploading and flips to Ready on the CDN put's ack.",
        "operationId": "deployments__create_deployment",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string"
                  },
                  "git_ref": {
                    "type": "string"
                  },
                  "commit_sha": {
                    "type": "string"
                  },
                  "size_bytes": {
                    "type": "integer"
                  },
                  "notes": {
                    "type": "string"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.deployments.list_deployments": {
      "post": {
        "tags": [
          "Deployments"
        ],
        "summary": "Deployment history for a Micro SaaS, newest first.",
        "description": "Deployment history for a Micro SaaS, newest first.",
        "operationId": "deployments__list_deployments",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.deployments.promote": {
      "post": {
        "tags": [
          "Deployments"
        ],
        "summary": "Flip a Ready deployment to Active. Deactivates any other Active row",
        "description": "Flip a Ready deployment to Active. Deactivates any other Active row\nfor the same Micro SaaS.",
        "operationId": "deployments__promote",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "version_key": {
                    "type": "string"
                  }
                },
                "required": [
                  "version_key"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.deployments.rollback": {
      "post": {
        "tags": [
          "Deployments"
        ],
        "summary": "Promote the most recent Rolled-Back deployment for this Micro SaaS.",
        "description": "Promote the most recent Rolled-Back deployment for this Micro SaaS.\nA convenience wrapper over `promote()` that finds the row to promote.",
        "operationId": "deployments__rollback",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  }
                },
                "required": [
                  "micro_saas"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.team.accept_invite": {
      "post": {
        "tags": [
          "Team"
        ],
        "summary": "Called by the invitee to activate their membership.",
        "description": "Called by the invitee to activate their membership.\n\nFlips the row from Invited to Active and grants the matching\n`Account <role>` Frappe role. The UP -> Account already exists from\ninvite time, so the role_guard's \"role requires matching UP\" check\npasses here.",
        "operationId": "team__accept_invite",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  }
                },
                "required": [
                  "account"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.team.invite_member": {
      "post": {
        "tags": [
          "Team"
        ],
        "summary": "Invite `email` to `account` as `account_role`. Creates the User if",
        "description": "Invite `email` to `account` as `account_role`. Creates the User if\nabsent, creates an Invited Account Membership row, sends the welcome\nemail (Frappe's built-in on User insert).\n\nRequires write on the Account. Refuses when Plan.max_team_seats is\nalready reached. Owner role is not invitable \u2014 transferred via a\nseparate flow.",
        "operationId": "team__invite_member",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "account_role": {
                    "type": "string"
                  }
                },
                "required": [
                  "account",
                  "email"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.team.list_members": {
      "post": {
        "tags": [
          "Team"
        ],
        "summary": "Everyone on the Account (permission-scoped). Includes Invited,",
        "description": "Everyone on the Account (permission-scoped). Includes Invited,\nActive, and Revoked rows so an Owner can see the full picture.",
        "operationId": "team__list_members",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "string"
                  }
                },
                "required": [
                  "account"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.sessions.list_my_sessions": {
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Every active session for the calling user.",
        "description": "Every active session for the calling user.\n\nIncludes the current session so a caller can spot it (by matching sid\nwith `frappe.session.sid`) and avoid revoking themself accidentally.",
        "operationId": "sessions__list_my_sessions",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.sessions.list_sessions_for": {
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Every active session for `user`. Staff-only.",
        "description": "Every active session for `user`. Staff-only.\n\nOwners use `list_my_sessions`; staff use this to see (e.g.) whose\nsession is still live before an impersonation ends.",
        "operationId": "sessions__list_sessions_for",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "user": {
                    "type": "string"
                  }
                },
                "required": [
                  "user"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.sessions.revoke_all_other_sessions": {
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Revoke every session for the calling user EXCEPT the current one.",
        "description": "Revoke every session for the calling user EXCEPT the current one.\n\nThe \"logout everywhere else\" button. Never touches other users'\nsessions -- staff who want that go through `revoke_session` per sid.",
        "operationId": "sessions__revoke_all_other_sessions",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.sessions.revoke_session": {
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Revoke one session by sid.",
        "description": "Revoke one session by sid.\n\nA user can revoke their own sessions freely. Only platform staff can\nrevoke someone else's. Every revoke emits a T2 Audit Event.",
        "operationId": "sessions__revoke_session",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "sid": {
                    "type": "string"
                  }
                },
                "required": [
                  "sid"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.approvals.approve": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "summary": "Approve a pending request. Approver must have write on the Micro",
        "description": "Approve a pending request. Approver must have write on the Micro\nSaaS the request targets \u2014 or Platform * role for a platform-wide\nrequest.",
        "operationId": "approvals__approve",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "approval_id": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "approval_id"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.approvals.deny": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "summary": "Deny",
        "description": "",
        "operationId": "approvals__deny",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "approval_id": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "approval_id"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.approvals.get_approval": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "summary": "Get Approval",
        "description": "",
        "operationId": "approvals__get_approval",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "approval_id": {
                    "type": "string"
                  }
                },
                "required": [
                  "approval_id"
                ]
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.approvals.list_pending": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "summary": "List Pending",
        "description": "",
        "operationId": "approvals__list_pending",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "micro_saas": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/method/platform_control.api.approvals.request_approval": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "summary": "Create a new Approval Request row and return its id. Agents call",
        "description": "Create a new Approval Request row and return its id. Agents call\nthis before executing any T3 action; the resulting id is the token the\napprover's decision keys off of.",
        "operationId": "approvals__request_approval",
        "responses": {
          "200": {
            "description": "Success. Response body varies per endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Typed error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TypedErrorEnvelope"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string"
                  },
                  "request_json": {
                    "type": "string"
                  },
                  "dry_run_diff": {
                    "type": "string"
                  },
                  "micro_saas": {
                    "type": "string"
                  },
                  "blast_tier": {
                    "type": "string"
                  },
                  "auto_approve_rule": {
                    "type": "string"
                  }
                },
                "required": [
                  "action"
                ]
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "TokenAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "`token <api_key>:<api_secret>` header."
      }
    },
    "schemas": {
      "TypedErrorEnvelope": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "code",
          "message",
          "hint",
          "docs_url"
        ]
      }
    }
  },
  "security": [
    {
      "TokenAuth": []
    }
  ]
}