{
  "openapi": "3.1.0",
  "info": {
    "title": "Obelisk API",
    "version": "obelisk-api-1",
    "summary": "Full-circle security for the agent era — a unified identity & security plane. Security you can prove, not just promise.",
    "description": "Passkey-first identity, one OIDC provider, tamper-evident receipts, a live security Rating, organizations, and a per-project profile layer. One identity across the whole ecosystem."
  },
  "servers": [
    {
      "url": "https://staging.obeliskgate.com"
    }
  ],
  "tags": [
    {
      "name": "release_history",
      "description": "Public-safe product history for humans and agents, bound to the revision serving it when deploy metadata is available."
    },
    {
      "name": "auth",
      "description": "Passkey-first sign-in + OIDC."
    },
    {
      "name": "fedcm",
      "description": "W272 — Federated Credential Management: the browser shows the Obelisk account chooser inline on a registered relying party's page and hands it a signed ID token. No redirect, no popup, third-party-cookie-proof. Discovery anchor: /.well-known/web-identity."
    },
    {
      "name": "agent_identity",
      "description": "Register, key-bind, verify, publish, suspend, recover, and revoke AI agents, bots, automations, devices, and agent teams without claiming that behavior proves non-humanness."
    },
    {
      "name": "account",
      "description": "The universal Obelisk account — one identity across every app."
    },
    {
      "name": "organizations",
      "description": "Register a business/website for its own Obelisk Rating; manage a team."
    },
    {
      "name": "app_registration",
      "description": "Self-serve OAuth/OIDC client registration — the developer portal (/account/apps) + its API."
    },
    {
      "name": "email_verification",
      "description": "Verify an address on the signed-in account without creating, merging or upgrading sessions."
    },
    {
      "name": "project_profile",
      "description": "The on-top layer: each project's own per-user profile slice, scoped to its OIDC client."
    },
    {
      "name": "studio_ecosystem",
      "description": "Studio-wide Obelisk rollout truth: declared, observed, required, migration cargo, and receipt-backed acknowledgement state."
    },
    {
      "name": "mcp",
      "description": "Obelisk's public Model Context Protocol tool server — read-only trust tools over JSON-RPC 2.0, plus the plain catalogue."
    },
    {
      "name": "proof",
      "description": "Public receipt-by-hash lookup with sorted-Merkle inclusion proofs against the tamper-evident ledger. By-hash only — no enumeration."
    },
    {
      "name": "federation",
      "description": "Obelisk as the SAML 2.0 IdP for downstream platforms (GitHub, Slack, AWS, …)."
    },
    {
      "name": "seal",
      "description": "The verified “Secured by Obelisk Gate” seal + per-site verification pages."
    },
    {
      "name": "scan",
      "description": "The public website posture scan (the same engine behind the org Rating)."
    },
    {
      "name": "contact",
      "description": "Reach the team."
    }
  ],
  "paths": {
    "/changelog": {
      "get": {
        "operationId": "get-changelog",
        "tags": [
          "release_history"
        ],
        "summary": "Human release history (HTML; versioned JSON with ?format=json or Accept: application/json).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/auth": {
      "get": {
        "operationId": "get-auth",
        "tags": [
          "auth"
        ],
        "summary": "The immersive sign-in/sign-up experience (HTML).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/auth/assertion-options": {
      "get": {
        "operationId": "get-auth-assertion-options",
        "tags": [
          "auth"
        ],
        "summary": "Begin a (usernameless) passkey sign-in.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "user",
            "in": "query",
            "required": false,
            "description": "optional account name",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/auth/assertion-verify": {
      "post": {
        "operationId": "post-auth-assertion-verify",
        "tags": [
          "auth"
        ],
        "summary": "Complete a passkey sign-in; returns the session + where to go (home).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user": {
                    "description": "optional"
                  },
                  "response": {
                    "description": "WebAuthn assertion"
                  },
                  "deviceId": {
                    "description": "optional"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/verify-session": {
      "post": {
        "operationId": "post-auth-verify-session",
        "tags": [
          "auth"
        ],
        "summary": "Server-to-server: validate a session token; returns { ok, identityId }.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "description": "macaroon"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/userinfo": {
      "get": {
        "operationId": "get-auth-userinfo",
        "tags": [
          "auth"
        ],
        "summary": "OIDC userinfo for an access token.",
        "x-obelisk-auth": "bearer",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/auth/magic/request": {
      "post": {
        "operationId": "post-auth-magic-request",
        "tags": [
          "auth"
        ],
        "summary": "Request an email magic-link (where enabled).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "description": "string",
                    "type": "string"
                  },
                  "dryRun": {
                    "description": "session-gated: return the link for the session's OWN verified email (founder at sensitive assurance: any email, receipted), don't send"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/magic": {
      "get": {
        "operationId": "get-auth-magic",
        "tags": [
          "auth"
        ],
        "summary": "W272 — the magic-link landing: consumes the token on the requesting device, then hands off (back to the relying party when one is waiting).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "description": "string",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "return",
            "in": "query",
            "required": false,
            "description": "validated relying-party return",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "project",
            "in": "query",
            "required": false,
            "description": "display name",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/auth/authorize": {
      "get": {
        "operationId": "get-auth-authorize",
        "tags": [
          "auth"
        ],
        "summary": "OIDC authorization endpoint (PKCE S256). W272: response_mode=web_message posts the code to the opener page from a popup; prompt=none|login; a live session fast-paths to one tap.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "required": false,
            "description": "registered client",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": false,
            "description": "exact registered callback",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "response_mode",
            "in": "query",
            "required": false,
            "description": "query | web_message",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "prompt",
            "in": "query",
            "required": false,
            "description": "none | login",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/auth/authorize/direct": {
      "post": {
        "operationId": "post-auth-authorize-direct",
        "tags": [
          "auth"
        ],
        "summary": "W272 — the direct grant: a passkey assertion (or fresh registration) performed ON a registered relying party's page (WebAuthn related origins) becomes a one-time authorization code. CORS-scoped to the origin registered for client_id; PKCE S256; auth-throttled.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "description": "string",
                    "type": "string"
                  },
                  "redirect_uri": {
                    "description": "string",
                    "type": "string"
                  },
                  "code_challenge": {
                    "description": "S256"
                  },
                  "code_challenge_method": {
                    "description": "S256"
                  },
                  "state": {
                    "description": "string",
                    "type": "string"
                  },
                  "nonce": {
                    "description": "optional"
                  },
                  "assertion": {
                    "description": "{ user?, response }"
                  },
                  "registration": {
                    "description": "{ user, response, signupToken? } — account creation in place"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/fedcm/config.json": {
      "get": {
        "operationId": "get-fedcm-config-json",
        "tags": [
          "fedcm"
        ],
        "summary": "Provider configuration (endpoints, branding, login_url).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/fedcm/accounts": {
      "get": {
        "operationId": "get-fedcm-accounts",
        "tags": [
          "fedcm"
        ],
        "summary": "The signed-in person's account(s); requires Sec-Fetch-Dest: webidentity (sent only by the browser's FedCM machinery).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/fedcm/client-metadata": {
      "get": {
        "operationId": "get-fedcm-client-metadata",
        "tags": [
          "fedcm"
        ],
        "summary": "Privacy/terms for a registered client; the request Origin must be registered to it.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "required": false,
            "description": "string",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/fedcm/assertion": {
      "post": {
        "operationId": "post-fedcm-assertion",
        "tags": [
          "fedcm"
        ],
        "summary": "Mint an ES256 ID token (aud = client_id, nonce-bound, truthful obelisk.assurance) for ONE registered client whose registered origin equals the request Origin.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "description": "the session's identity"
                  },
                  "client_id": {
                    "description": "string",
                    "type": "string"
                  },
                  "nonce": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/fedcm/disconnect": {
      "post": {
        "operationId": "post-fedcm-disconnect",
        "tags": [
          "fedcm"
        ],
        "summary": "The person asked the browser to forget the link; acknowledged and receipted.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "description": "string",
                    "type": "string"
                  },
                  "account_hint": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/account/agents": {
      "get": {
        "operationId": "get-account-agents",
        "tags": [
          "agent_identity"
        ],
        "summary": "Agent Studio (HTML): the owner's whole agent fleet — create, import, pair, and manage.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/account/agents/{agentId}": {
      "get": {
        "operationId": "get-account-agents-agentId",
        "tags": [
          "agent_identity"
        ],
        "summary": "One agent's deep-linkable profile page (HTML): declared runtime facts, proof keys, evidence, delegation, and the full management card. Unknown or foreign ids 404 identically.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/auth/agents/proof/enroll/begin": {
      "post": {
        "operationId": "post-auth-agents-proof-enroll-begin",
        "tags": [
          "agent_identity"
        ],
        "summary": "Begin device-style enrollment with an agent-generated public JWK. Returns a short owner code; private key material is refused.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "string",
                    "type": "string"
                  },
                  "subjectKind": {
                    "description": "service_bot|automation|ai_agent|hybrid_agent|robot|device|agent_team|ephemeral_subagent"
                  },
                  "publicJwk": {
                    "description": "P-256/Ed25519 public JWK"
                  },
                  "visibility": {
                    "description": "private|unlisted|public"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/enroll/approve": {
      "post": {
        "operationId": "post-auth-agents-proof-enroll-approve",
        "tags": [
          "agent_identity"
        ],
        "summary": "Passkey-authorized accountable party approves the pending agent; identity remains pending until the agent proves its key.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enrollment_id": {
                    "description": "aen_..."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/enroll/complete": {
      "post": {
        "operationId": "post-auth-agents-proof-enroll-complete",
        "tags": [
          "agent_identity"
        ],
        "summary": "Agent completes enrollment with a nonce-bound ES256 DPoP header. Returns its opaque Proof Link.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enrollment_id": {
                    "description": "aen_..."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/enroll/status": {
      "post": {
        "operationId": "post-auth-agents-proof-enroll-status",
        "tags": [
          "agent_identity"
        ],
        "summary": "Poll bounded enrollment state without learning owner details.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enrollment_id": {
                    "description": "aen_..."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/agent/{proof_id}": {
      "get": {
        "operationId": "get-agent-proof-id",
        "tags": [
          "agent_identity"
        ],
        "summary": "Human-readable opt-in Proof Link. Private profiles 404; a URL is never a credential.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "proof_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/agent/{proof_id}.json": {
      "get": {
        "operationId": "get-agent-proof-id-json",
        "tags": [
          "agent_identity"
        ],
        "summary": "Machine proof vector plus W3C VC and A2A projections. Claims remain independent and freshness-scoped.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "proof_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/agent-proof/challenge": {
      "post": {
        "operationId": "post-api-agent-proof-challenge",
        "tags": [
          "agent_identity"
        ],
        "summary": "Request a short-lived nonce for a visible active agent.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "proof_id": {
                    "description": "opaque id"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/agent-proof/verify": {
      "post": {
        "operationId": "post-api-agent-proof-verify",
        "tags": [
          "agent_identity"
        ],
        "summary": "Verify current agent-key control with a one-use challenge and DPoP; returns a short-lived signed proof token when signing is available.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "proof_id": {
                    "description": "opaque id"
                  },
                  "challenge_id": {
                    "description": "apc_..."
                  },
                  "audience": {
                    "description": "optional verifier audience"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/agent-proof/run": {
      "post": {
        "operationId": "post-api-agent-proof-run",
        "tags": [
          "agent_identity"
        ],
        "summary": "Bind a client-built, commitment-only run proof to the active agent key and one-use nonce; returns a short-lived Obelisk-signed run token. Raw task/input/output stay client-side.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "proof_id": {
                    "description": "opaque id"
                  },
                  "challenge_id": {
                    "description": "purpose:run challenge"
                  },
                  "run_proof": {
                    "description": "obelisk-agent-run-proof-v1"
                  },
                  "audience": {
                    "description": "optional verifier audience"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/visibility": {
      "post": {
        "operationId": "post-auth-agents-proof-visibility",
        "tags": [
          "agent_identity"
        ],
        "summary": "Set profile privacy: private, unlisted, or public. Requires passkey-grade step-up.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/agents/proof/lifecycle": {
      "post": {
        "operationId": "post-auth-agents-proof-lifecycle",
        "tags": [
          "agent_identity"
        ],
        "summary": "Suspend, resume, mark compromised, or revoke. Compromise/revoke cascades to agent-bound API keys.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/agents/proof/key-rotate": {
      "post": {
        "operationId": "post-auth-agents-proof-key-rotate",
        "tags": [
          "agent_identity"
        ],
        "summary": "Rotate a public key. Normal rotation requires proof of the current key; compromise recovery rotates first and resumes only from suspended state.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/agents/proof/transfer/begin": {
      "post": {
        "operationId": "post-auth-agents-proof-transfer-begin",
        "tags": [
          "agent_identity"
        ],
        "summary": "Source owner suspends an agent and creates a short transfer code for one named target owner.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/agents/proof/transfer/accept": {
      "post": {
        "operationId": "post-auth-agents-proof-transfer-accept",
        "tags": [
          "agent_identity"
        ],
        "summary": "Named target owner accepts with a separate passkey and fresh public JWK. The identity stays suspended until target passkey + new agent key co-sign resume.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/agents/proof/dns-challenge": {
      "post": {
        "operationId": "post-auth-agents-proof-dns-challenge",
        "tags": [
          "agent_identity"
        ],
        "summary": "Get the stable _obelisk-agent TXT record that proves domain control for this agent. Publish it, then dns-verify.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "description": "agt_..."
                  },
                  "domain": {
                    "description": "example.com"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/dns-verify": {
      "post": {
        "operationId": "post-auth-agents-proof-dns-verify",
        "tags": [
          "agent_identity"
        ],
        "summary": "Resolve the TXT record and, on match, append domain-control evidence (channel_control leaves 'declared').",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "description": "agt_..."
                  },
                  "domain": {
                    "description": "example.com"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/agents/proof/a2a-discover": {
      "post": {
        "operationId": "post-auth-agents-proof-a2a-discover",
        "tags": [
          "agent_identity"
        ],
        "summary": "Fetch an A2A agent card (SSRF-guarded, 128KB cap) and append descriptor-integrity evidence; fills declared a2aCardUrl/capabilities only where empty.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "description": "agt_..."
                  },
                  "url": {
                    "description": "https card URL"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/agent-trust/bundle": {
      "get": {
        "operationId": "get-api-agent-trust-bundle",
        "tags": [
          "agent_identity"
        ],
        "summary": "This authority's claim registry as a signed trust bundle (JWS via jwks_uri) for federation peers to pin and import.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/account": {
      "get": {
        "operationId": "get-account",
        "tags": [
          "account"
        ],
        "summary": "Your account page (HTML; JSON with ?format=json → the full profile).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/account/export": {
      "get": {
        "operationId": "get-account-export",
        "tags": [
          "account"
        ],
        "summary": "Download a portable, secret-free copy of your account (JSON).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/profile": {
      "post": {
        "operationId": "post-auth-profile",
        "tags": [
          "account"
        ],
        "summary": "Set your display name.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "displayName": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/account/rename": {
      "post": {
        "operationId": "post-auth-account-rename",
        "tags": [
          "account"
        ],
        "summary": "Change your username (old name stays a resolvable alias).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "newUsername": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/preferences": {
      "post": {
        "operationId": "post-auth-preferences",
        "tags": [
          "account"
        ],
        "summary": "Merge your preferences (theme/lang/contrast/motion/text/email).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "preferences": {
                    "description": "object",
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/passkeys/list": {
      "post": {
        "operationId": "post-auth-passkeys-list",
        "tags": [
          "account"
        ],
        "summary": "List your passkeys (no public keys).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/passkeys/rename": {
      "post": {
        "operationId": "post-auth-passkeys-rename",
        "tags": [
          "account"
        ],
        "summary": "Rename a passkey.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "credId": {
                    "description": "string",
                    "type": "string"
                  },
                  "label": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/passkeys/remove": {
      "post": {
        "operationId": "post-auth-passkeys-remove",
        "tags": [
          "account"
        ],
        "summary": "Remove a passkey (never the last one).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "credId": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/passkeys/add-options": {
      "post": {
        "operationId": "post-auth-passkeys-add-options",
        "tags": [
          "account"
        ],
        "summary": "Begin adding a passkey to your account.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/passkeys/add-verify": {
      "post": {
        "operationId": "post-auth-passkeys-add-verify",
        "tags": [
          "account"
        ],
        "summary": "Finish adding a passkey.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "response": {
                    "description": "WebAuthn attestation"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/totp/setup": {
      "post": {
        "operationId": "post-auth-totp-setup",
        "tags": [
          "account"
        ],
        "summary": "Begin TOTP setup (pending) → { secret, uri, qrSvg }.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/totp/confirm": {
      "post": {
        "operationId": "post-auth-totp-confirm",
        "tags": [
          "account"
        ],
        "summary": "Confirm + activate TOTP with a code.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "description": "6 digits"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/totp/disable": {
      "post": {
        "operationId": "post-auth-totp-disable",
        "tags": [
          "account"
        ],
        "summary": "Turn off TOTP.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/recovery/regenerate": {
      "post": {
        "operationId": "post-auth-recovery-regenerate",
        "tags": [
          "account"
        ],
        "summary": "Generate new backup codes (shown once).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/sessions/revoke": {
      "post": {
        "operationId": "post-auth-sessions-revoke",
        "tags": [
          "account"
        ],
        "summary": "Revoke a session.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "identifier": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/devices/revoke": {
      "post": {
        "operationId": "post-auth-devices-revoke",
        "tags": [
          "account"
        ],
        "summary": "Sign out a whole device — revokes every session it holds, then drops it from the device ledger.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "deviceId": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/signout": {
      "post": {
        "operationId": "post-auth-signout",
        "tags": [
          "account"
        ],
        "summary": "Clear the session cookie.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/orgs/create": {
      "post": {
        "operationId": "post-auth-orgs-create",
        "tags": [
          "organizations"
        ],
        "summary": "Create an organization (you become owner).",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "string",
                    "type": "string"
                  },
                  "website": {
                    "description": "optional url"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}": {
      "get": {
        "operationId": "get-org-slug",
        "tags": [
          "organizations"
        ],
        "summary": "Org dashboard (HTML; JSON with ?format=json), member-gated.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/org/{slug}/scan": {
      "post": {
        "operationId": "post-org-slug-scan",
        "tags": [
          "organizations"
        ],
        "summary": "Run an external security-posture scan → an Obelisk Rating. Scan a specific site with { siteId } or the primary by default.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "website": {
                    "description": "optional override"
                  },
                  "siteId": {
                    "description": "optional registered-site id"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/websites/add": {
      "post": {
        "operationId": "post-org-slug-websites-add",
        "tags": [
          "organizations"
        ],
        "summary": "Add a website this org owns (first becomes primary).",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "description": "https url"
                  },
                  "label": {
                    "description": "optional"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/websites/remove": {
      "post": {
        "operationId": "post-org-slug-websites-remove",
        "tags": [
          "organizations"
        ],
        "summary": "Remove a registered site by id.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "siteId": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/websites/primary": {
      "post": {
        "operationId": "post-org-slug-websites-primary",
        "tags": [
          "organizations"
        ],
        "summary": "Promote a site to primary (the org headline rating tracks it).",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "siteId": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/members/role": {
      "post": {
        "operationId": "post-org-slug-members-role",
        "tags": [
          "organizations"
        ],
        "summary": "Change a member's role. Owner-only for owner changes; the last owner can't be demoted.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user": {
                    "description": "identityId"
                  },
                  "role": {
                    "description": "member|admin|owner"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/members/remove": {
      "post": {
        "operationId": "post-org-slug-members-remove",
        "tags": [
          "organizations"
        ],
        "summary": "Remove a member. The last owner can never be removed.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user": {
                    "description": "identityId"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/invite": {
      "post": {
        "operationId": "post-org-slug-invite",
        "tags": [
          "organizations"
        ],
        "summary": "Create a role-scoped invite link.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "description": "member|admin"
                  },
                  "email": {
                    "description": "optional"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/invite/revoke": {
      "post": {
        "operationId": "post-org-slug-invite-revoke",
        "tags": [
          "organizations"
        ],
        "summary": "Revoke an invite.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/settings": {
      "post": {
        "operationId": "post-org-slug-settings",
        "tags": [
          "organizations"
        ],
        "summary": "Rename the organization and/or set its description. The @slug handle is permanent; member profiles update automatically.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "string ≤80",
                    "type": "string"
                  },
                  "description": {
                    "description": "optional string ≤280"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/org/{slug}/leave": {
      "post": {
        "operationId": "post-org-slug-leave",
        "tags": [
          "organizations"
        ],
        "summary": "Leave the organization (self-service). The last owner must transfer ownership first; your agents scoped to it become personal.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/org/{slug}/transfer-ownership": {
      "post": {
        "operationId": "post-org-slug-transfer-ownership",
        "tags": [
          "organizations"
        ],
        "summary": "Hand ownership to an existing member (passkey step-up required). You stay on as admin.",
        "x-obelisk-auth": "owner",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user": {
                    "description": "identityId"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/join/{code}": {
      "get": {
        "operationId": "get-join-code",
        "tags": [
          "organizations"
        ],
        "summary": "Accept an invite (signs you in first if needed).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/account/apps": {
      "get": {
        "operationId": "get-account-apps",
        "tags": [
          "app_registration"
        ],
        "summary": "The hosted app-registration portal (HTML): register apps, list, copy config, revoke.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/register": {
      "post": {
        "operationId": "post-auth-register",
        "tags": [
          "app_registration"
        ],
        "summary": "RFC 7591 dynamic client registration → { client_id, ... }. Optional { org } scopes the app to an organization you administer (else your personal tenant). Requires OBELISK_DCR=open + tenant enforcement.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_name": {
                    "description": "string",
                    "type": "string"
                  },
                  "redirect_uris": {
                    "description": "https[]"
                  },
                  "obelisk_project_trust_profile": {
                    "description": "object",
                    "type": "object"
                  },
                  "resource_uris": {
                    "description": "https[]"
                  },
                  "org": {
                    "description": "optional org slug"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/apps/list": {
      "post": {
        "operationId": "post-auth-apps-list",
        "tags": [
          "app_registration"
        ],
        "summary": "List apps in tenants you control (personal + admin orgs). Never returns a secret.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/apps/revoke": {
      "post": {
        "operationId": "post-auth-apps-revoke",
        "tags": [
          "app_registration"
        ],
        "summary": "Revoke an app you own → status:revoked.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "clientId": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/apps/update": {
      "post": {
        "operationId": "post-auth-apps-update",
        "tags": [
          "app_registration"
        ],
        "summary": "Update an owned app name, exact callbacks, resources or trust profile. Requires verified assurance and the latest list revision; stale edits return 409.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "clientId": {
                    "description": "string",
                    "type": "string"
                  },
                  "expectedRevision": {
                    "description": "integer"
                  },
                  "client_name": {
                    "description": "optional string"
                  },
                  "redirect_uris": {
                    "description": "optional https[]"
                  },
                  "resource_uris": {
                    "description": "optional https[]"
                  },
                  "obelisk_project_trust_profile": {
                    "description": "optional object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/apps/rotate-secret": {
      "post": {
        "operationId": "post-auth-apps-rotate-secret",
        "tags": [
          "app_registration"
        ],
        "summary": "Rotate an owned confidential client secret. Returns it once; the previous secret immediately stops working. Requires verified assurance and current revision.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "clientId": {
                    "description": "string",
                    "type": "string"
                  },
                  "expectedRevision": {
                    "description": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/email": {
      "get": {
        "operationId": "get-auth-email",
        "tags": [
          "email_verification"
        ],
        "summary": "Hosted email verification and same-browser confirmation page.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ]
      }
    },
    "/auth/email/request": {
      "post": {
        "operationId": "post-auth-email-request",
        "tags": [
          "email_verification"
        ],
        "summary": "Email a five-minute, single-use proof bound to the current strong session and browser. Rate limited; never returns the proof.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "description": "email address"
                  },
                  "codeChallenge": {
                    "description": "S256 base64url challenge"
                  },
                  "deviceNonce": {
                    "description": "random browser nonce"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/email/confirm": {
      "post": {
        "operationId": "post-auth-email-confirm",
        "tags": [
          "email_verification"
        ],
        "summary": "Confirm email possession for the same strong session. Rejects replay, expiry and another account's address; never mints a session.",
        "x-obelisk-auth": "session",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "proofToken": {
                    "description": "emailed proof"
                  },
                  "codeVerifier": {
                    "description": "PKCE verifier"
                  },
                  "deviceNonce": {
                    "description": "requesting browser nonce"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/project-profile": {
      "get": {
        "operationId": "get-api-project-profile",
        "tags": [
          "project_profile"
        ],
        "summary": "Read this project's profile slice for the token's user → { sub, project, profile }.",
        "x-obelisk-auth": "bearer",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "post-api-project-profile",
        "tags": [
          "project_profile"
        ],
        "summary": "Shallow-merge into this project's slice.",
        "x-obelisk-auth": "bearer",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "profile": {
                    "description": "object",
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/studio/ecosystem/manifest.json": {
      "get": {
        "operationId": "get-studio-ecosystem-manifest-json",
        "tags": [
          "studio_ecosystem"
        ],
        "summary": "Fleet manifest for Studio Ops: every registered project, required Obelisk versions, observed status, and migration state.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/studio/ecosystem/projects/{slug}": {
      "get": {
        "operationId": "get-studio-ecosystem-projects-slug",
        "tags": [
          "studio_ecosystem"
        ],
        "summary": "One project's ecosystem profile row from the canonical manifest.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/studio/ecosystem/projects/{slug}/migration": {
      "get": {
        "operationId": "get-studio-ecosystem-projects-slug-migration",
        "tags": [
          "studio_ecosystem"
        ],
        "summary": "Content-addressed recipient-owned Ark migration cargo for one project.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/studio/ecosystem/projects/{slug}/ack": {
      "post": {
        "operationId": "post-studio-ecosystem-projects-slug-ack",
        "tags": [
          "studio_ecosystem"
        ],
        "summary": "Record a recipient-owned migration acknowledgement. Requires x-obelisk-ecosystem-ack; writes an ecosystem-ack receipt.",
        "x-obelisk-auth": "ack-secret",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "result": {
                    "description": "applied|already-current|rejected|blocked"
                  },
                  "cargoDigest": {
                    "description": "sha256:..."
                  },
                  "repoCommit": {
                    "description": "optional commit"
                  },
                  "evidence": {
                    "description": "optional object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "post-mcp",
        "tags": [
          "mcp"
        ],
        "summary": "JSON-RPC 2.0 MCP endpoint (initialize, tools/list, tools/call). Read-only trust tools; stateful per-client rate limit.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "jsonrpc": {
                    "description": "the literal \"2.0\""
                  },
                  "id": {
                    "description": "number|string"
                  },
                  "method": {
                    "description": "initialize | tools/list | tools/call"
                  },
                  "params": {
                    "description": "tools/call: { name, arguments }"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/mcp/tools": {
      "get": {
        "operationId": "get-mcp-tools",
        "tags": [
          "mcp"
        ],
        "summary": "The plain JSON tool catalogue: every tool's name, description, and input schema.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/api/proof": {
      "get": {
        "operationId": "get-api-proof",
        "tags": [
          "proof"
        ],
        "summary": "Self-describing: how to verify, and where the transparency head to pin lives.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/api/proof/{hash}": {
      "get": {
        "operationId": "get-api-proof-hash",
        "tags": [
          "proof"
        ],
        "summary": "Receipt lookup by 64-hex currentHash → chain position + a Merkle inclusion proof {leafHash, path, root}. Hash material only; never bodies, actors, or intents.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/saml/idp/metadata": {
      "get": {
        "operationId": "get-saml-idp-metadata",
        "tags": [
          "federation"
        ],
        "summary": "IdP metadata: entity ID, SSO URL, and the pinned X.509 signing certificate — hand this URL to any SAML service provider.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/saml/idp/sso": {
      "get": {
        "operationId": "get-saml-idp-sso",
        "tags": [
          "federation"
        ],
        "summary": "SAML sign-on endpoint: authenticates passkey-first, then auto-POSTs an RSA-SHA256-signed assertion to the SP's ACS.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "SAMLRequest",
            "in": "query",
            "required": false,
            "description": "SP AuthnRequest (redirect binding)",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/embed/seal.js": {
      "get": {
        "operationId": "get-embed-seal-js",
        "tags": [
          "seal"
        ],
        "summary": "Drop-in loader: one script tag renders the live seal card on a relying party's page.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/embed/v1/seal": {
      "get": {
        "operationId": "get-embed-v1-seal",
        "tags": [
          "seal"
        ],
        "summary": "The framed seal card itself (rendered live from obeliskgate.com; can't be forged).",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        }
      }
    },
    "/verify/{rp}": {
      "get": {
        "operationId": "get-verify-rp",
        "tags": [
          "seal"
        ],
        "summary": "Public verification page for a relying party — anyone can confirm the seal is genuine.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "parameters": [
          {
            "name": "rp",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/scan": {
      "post": {
        "operationId": "post-api-scan",
        "tags": [
          "scan"
        ],
        "summary": "Scan a public https:// website's security posture → a rating with concrete levers. SSRF-guarded, throttled per client.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "description": "public https:// website (domains only)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "post-api-contact",
        "tags": [
          "contact"
        ],
        "summary": "Send a message to the team (form-encoded: name, email, subject, message). Honeypot-protected, throttled.",
        "x-obelisk-auth": "none",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "description": "Error — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it."
          }
        },
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "description": "string",
                    "type": "string"
                  },
                  "email": {
                    "description": "string",
                    "type": "string"
                  },
                  "subject": {
                    "description": "string?",
                    "type": "string"
                  },
                  "message": {
                    "description": "string",
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "obelisk_session",
        "description": "obelisk_session cookie OR body.token"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "OIDC access token (Authorization: Bearer). Operations marked x-obelisk-auth:\"owner\" additionally require: session + org owner/admin."
      }
    }
  }
}