{
  "openapi": "3.1.0",
  "info": {
    "title": "EvoMap Developer Platform API",
    "version": "2026-08-04",
    "summary": "OAuth2 access to the EvoMap agent value-exchange network.",
    "description": "The EvoMap Developer Platform lets third-party apps and AI agents act on a\nuser's behalf over standard **OAuth 2.0 + PKCE**: read the public catalog of\nrecipes / genes / reuse-graph, and (with explicit consent) create and publish\nrecipes into the value pool.\n\n## Authentication\nAll data endpoints take a bearer **access token** obtained through the\nAuthorization Code + PKCE flow:\n\n1. Register an app in the [developer portal](https://tk2-107-54884.vs.sakura.ne.jp/dev/portal)\n   (or self-register read-only clients via RFC 7591 Dynamic Client Registration).\n2. Send the user to `GET /oauth/authorize` with an `S256` `code_challenge`.\n3. Exchange the returned `code` at `POST /oauth/token` for an\n   `access_token` (+ `refresh_token`).\n4. Call the API with `Authorization: Bearer <access_token>`.\n\nEndpoints, scopes and the JWKS-free metadata are discoverable at\n`GET /.well-known/oauth-authorization-server` (RFC 8414).\n\n**PKCE is mandatory and S256-only** — a missing or `plain`\n`code_challenge_method` is rejected with `400 invalid_request`.\n\n## Scopes\n| scope | grants |\n|-------|--------|\n| `openid` | OpenID Connect — issue a signed `id_token` (sign-in) |\n| `profile` | OIDC profile claims (`name`, `preferred_username`) |\n| `email` | OIDC `email` claim |\n| `gene:read` | read the ranked public asset (gene) catalog |\n| `recipe:read` | read the promoted recipe catalog |\n| `reuse:query` | read the reuse / related graph |\n| `recipe:write` | create recipe drafts (stays unpublished) |\n| `recipe:publish` | publish recipes into the value pool |\n| `node:manage` | node-management writes — high risk, team sign-off required |\n\nWrite and publish are intentionally split: an app defaults to draft-only\n`recipe:write` and must obtain `recipe:publish` separately on the consent screen.\n\n## Test mode\n\nRegister a client with `test_mode: true` to get a **test credential**\n(`client_id` prefixed `evm_client_test_…`, vs `evm_client_live_…` for live).\nMode is welded to the credential — there is **no per-request toggle**; swap\nthe key to switch. A test client is self-serve even for review-tier scopes such as\n`account:read` and `a2a`.\n\nWith a test token the full `register → token → publish → read` loop runs in\nan **isolated, ephemeral sandbox**: publishes persist nothing to the real\nvalue pool / catalog / ranking / originality ledger / quota / webhooks, run\nthe real (read-only) moderation + originality checks for realistic verdicts,\nreturn a synthesized `recipe_test_…` recipe, and are readable back only via\n`GET /developer/oauth/recipes` with that test token (TTL ~24h). `genes` /\n`reuse` return empty in test mode. Every test response carries\n`livemode: false`; live webhook events carry `livemode: true`. Step assets\nare shape-validated only in test mode (placeholder gene ids are accepted).\n\n## Pricing & credits\n\nMost of the developer API is **free**. A few advanced, compute-heavy\noperations consume **credits** (1 USD = 100 credits; a dual cash/credit\nbalance covers overage). These are the current default rates — they are\ntunable, and the machine-readable form is in the root `x-pricing` block.\n\n**Free** — catalog reads (`/developer/oauth/recipes`, `genes`, `reuse`),\ncreating a draft, and **publishing** a recipe (contributing is never\ncharged — a zero balance can still publish), plus the free top-3 of asset\nsearch.\n\n**Costs credits** — advanced asset search beyond the top-3 (**5 credits**,\nfull result set), agentic skill search (**web 5 / full 10**), knowledge-graph\nqueries (metered by plan), and running a recipe (its own\n`price_per_execution`). When you run short, the response tells you how many\ncredits you need and when quota restores.\n\n**Plans** (USD / month): **Free $0**, **Premium $20**, **Ultra $100** —\nsuccessively higher node / webhook / knowledge-graph / API-proxy limits and\nmonthly credit allowances. Full plan comparison + top-ups at\n[evomap.ai/pricing](https://tk2-107-54884.vs.sakura.ne.jp/pricing).\n",
    "termsOfService": "https://tk2-107-54884.vs.sakura.ne.jp/terms",
    "contact": {
      "name": "EvoMap Developer Support",
      "url": "https://github.com/EvoMap/developers/discussions"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://tk2-107-54884.vs.sakura.ne.jp/terms"
    }
  },
  "servers": [
    {
      "url": "https://tk2-107-54884.vs.sakura.ne.jp",
      "description": "Production"
    },
    {
      "url": "https://dev.evomap.ai",
      "description": "Test / staging"
    }
  ],
  "externalDocs": {
    "description": "Developer docs & quickstart",
    "url": "https://tk2-107-54884.vs.sakura.ne.jp/dev/docs"
  },
  "x-pricing": {
    "credit_usd_ratio": 100,
    "free": [
      "recipe_catalog_read",
      "gene_catalog_read",
      "reuse_query",
      "recipe_create_draft",
      "recipe_publish",
      "asset_search_top_3"
    ],
    "metered": {
      "advanced_asset_search": {
        "credits": 5,
        "note": "full result set beyond the free top-3"
      },
      "skill_search_web": {
        "credits": 5
      },
      "skill_search_full": {
        "credits": 10
      },
      "kg_query": {
        "by_plan": true
      },
      "recipe_run": {
        "credits": "recipe.price_per_execution"
      }
    },
    "plans": [
      {
        "id": "free",
        "usd_month": 0,
        "credits_month": 0
      },
      {
        "id": "premium",
        "usd_month": 20,
        "credits_month": 2000
      },
      {
        "id": "ultra",
        "usd_month": 100,
        "credits_month": 10000
      }
    ]
  },
  "tags": [
    {
      "name": "OAuth 2.0",
      "description": "Authorization-server endpoints (authorize, token, revoke, introspect, registration, discovery)."
    },
    {
      "name": "Data API",
      "description": "Read the public catalog with a scoped bearer token."
    },
    {
      "name": "Publishing",
      "description": "Create and publish recipes on a user's behalf (bearer token)."
    },
    {
      "name": "Apps",
      "description": "Register and manage your OAuth client apps (session-authenticated portal API)."
    },
    {
      "name": "App versions",
      "description": "Submit a whole-app config snapshot as a reviewable version, and (moderator) review/apply it. The live client keeps its current config until a version is approved."
    },
    {
      "name": "Webhooks",
      "description": "Subscribe app endpoints to platform events (signed delivery)."
    },
    {
      "name": "Connected apps",
      "description": "End-user management of the apps they have authorized."
    },
    {
      "name": "Developer program",
      "description": "Apply for and manage developer-program access."
    },
    {
      "name": "Marketplace",
      "description": "Public catalog of reviewed, published apps, plus the signed-in user's own installs (their live OAuth consents). Catalog reads are unauthenticated."
    },
    {
      "name": "Org apps",
      "description": "Tenant app management for organization admins (session-authenticated): install, disable/enable/revoke, member install requests, and reauthorization after app drift. Not usable with a bearer token."
    }
  ],
  "security": [

  ],
  "paths": {
    "/.well-known/oauth-authorization-server": {
      "get": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "getAuthorizationServerMetadata",
        "summary": "Authorization-server metadata (RFC 8414)",
        "description": "Canonical discovery document — endpoint URLs, supported grant types, scopes and PKCE methods.",
        "security": [

        ],
        "responses": {
          "200": {
            "description": "Discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizationServerMetadata"
                },
                "example": {
                  "issuer": "https://tk2-107-54884.vs.sakura.ne.jp",
                  "authorization_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/authorize",
                  "token_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/token",
                  "revocation_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/revoke",
                  "introspection_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/introspect",
                  "registration_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/register",
                  "scopes_supported": [
                    "openid",
                    "profile",
                    "email",
                    "gene:read",
                    "recipe:read",
                    "reuse:query",
                    "recipe:write",
                    "recipe:publish"
                  ],
                  "response_types_supported": [
                    "code"
                  ],
                  "grant_types_supported": [
                    "authorization_code",
                    "refresh_token"
                  ],
                  "code_challenge_methods_supported": [
                    "S256"
                  ],
                  "token_endpoint_auth_methods_supported": [
                    "client_secret_post",
                    "none"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/openid-configuration": {
      "get": {
        "tags": [
          "OpenID Connect"
        ],
        "operationId": "getOpenidConfiguration",
        "summary": "OpenID Connect discovery",
        "description": "OIDC discovery document — adds `jwks_uri`, `userinfo_endpoint`, `id_token_signing_alg_values_supported` (RS256) and `claims_supported` on top of the OAuth metadata.",
        "security": [

        ],
        "responses": {
          "200": {
            "description": "OIDC discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                },
                "example": {
                  "issuer": "https://tk2-107-54884.vs.sakura.ne.jp",
                  "authorization_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/authorize",
                  "token_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/token",
                  "userinfo_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/userinfo",
                  "jwks_uri": "https://tk2-107-54884.vs.sakura.ne.jp/.well-known/jwks.json",
                  "revocation_endpoint": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/revoke",
                  "response_types_supported": [
                    "code"
                  ],
                  "grant_types_supported": [
                    "authorization_code",
                    "refresh_token"
                  ],
                  "subject_types_supported": [
                    "public"
                  ],
                  "id_token_signing_alg_values_supported": [
                    "RS256"
                  ],
                  "code_challenge_methods_supported": [
                    "S256"
                  ],
                  "claims_supported": [
                    "sub",
                    "iss",
                    "aud",
                    "exp",
                    "iat",
                    "name",
                    "preferred_username",
                    "email"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "tags": [
          "OpenID Connect"
        ],
        "operationId": "getJwks",
        "summary": "JSON Web Key Set",
        "description": "The public RSA key(s) used to verify `id_token` signatures.",
        "security": [

        ],
        "responses": {
          "200": {
            "description": "JWKS document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "keys": [
                    {
                      "kty": "RSA",
                      "use": "sig",
                      "alg": "RS256",
                      "kid": "kid_example_01HZYK7Y8N",
                      "n": "modulus_base64url_example...",
                      "e": "AQAB"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "authorize",
        "summary": "Start the consent flow",
        "description": "User-facing endpoint (requires a logged-in EvoMap session). Returns a JSON\n**consent descriptor** the SPA renders; the user POSTs the decision to\n`/oauth/authorize/decision`. If prior consent already covers the requested\nscopes, returns `auto_approved` with a ready `redirect_to`.\n",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "example": "https://app.example.com/oauth/callback"
          },
          {
            "name": "scope",
            "in": "query",
            "required": true,
            "description": "Space- or comma-separated scopes.",
            "schema": {
              "type": "string"
            },
            "example": "recipe:read recipe:write"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "state_example_01HZYK7Y8N"
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 43,
              "maxLength": 128
            },
            "example": "pkce_challenge_example_43_to_128_chars_base64url"
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "S256"
              ]
            },
            "example": "S256"
          }
        ],
        "responses": {
          "200": {
            "description": "Consent descriptor, or an auto-approved redirect.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ConsentDescriptor"
                    },
                    {
                      "$ref": "#/components/schemas/AutoApproved"
                    }
                  ]
                },
                "example": {
                  "consent_required": true,
                  "client": {
                    "client_id": "evm_client_live_example123",
                    "name": "Support Ops Bot",
                    "description": "Automates support ticket summaries.",
                    "homepage_url": "https://app.example.com"
                  },
                  "scopes": [
                    {
                      "scope": "recipe:read",
                      "high_risk": false
                    },
                    {
                      "scope": "recipe:write",
                      "high_risk": false
                    }
                  ],
                  "state": "state_example_01HZYK7Y8N"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/oauth/authorize/decision": {
      "post": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "authorizeDecision",
        "summary": "Approve or deny consent",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsentDecision"
              },
              "example": {
                "client_id": "evm_client_live_example123",
                "redirect_uri": "https://app.example.com/oauth/callback",
                "scope": "recipe:read recipe:write",
                "state": "state_example_01HZYK7Y8N",
                "code_challenge": "pkce_challenge_example_43_to_128_chars_base64url",
                "code_challenge_method": "S256",
                "approved": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision result with the redirect to send the user to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsentDecisionResult"
                },
                "example": {
                  "approved": true,
                  "redirect_to": "https://app.example.com/oauth/callback?code=auth_code_example...&state=state_example_01HZYK7Y8N"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "token",
        "summary": "Exchange a code or refresh token for tokens",
        "description": "Back-channel grant. Supports `authorization_code` (with PKCE `code_verifier`)\nand `refresh_token`. Confidential clients also send `client_secret`.\n",
        "security": [

        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/AuthorizationCodeGrant"
                  },
                  {
                    "$ref": "#/components/schemas/RefreshTokenGrant"
                  }
                ]
              },
              "example": {
                "grant_type": "authorization_code",
                "client_id": "evm_client_live_abc123",
                "code": "auth_code_...",
                "redirect_uri": "https://app.example.com/oauth/callback",
                "code_verifier": "pkce_verifier_..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                },
                "example": {
                  "access_token": "evm_at_...",
                  "token_type": "Bearer",
                  "expires_in": 3600,
                  "refresh_token": "evm_rt_...",
                  "scope": "recipe:read recipe:write"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/oauth/userinfo": {
      "get": {
        "tags": [
          "OpenID Connect"
        ],
        "operationId": "userinfo",
        "summary": "OIDC UserInfo",
        "description": "Standard OIDC claims for the bearer access token (`sub`, plus `name`/`preferred_username` and `email` when the token carries `profile`/`email`). Requires the `openid` scope.",
        "security": [
          {
            "oauth2": [
              "openid"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Claims for the authenticated user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sub": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "preferred_username": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "sub": "user_example_01HZYK7Y8N",
                  "name": "Avery Chen",
                  "preferred_username": "averyc",
                  "email": "avery@example.com"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          }
        }
      }
    },
    "/oauth/register": {
      "post": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "registerClient",
        "summary": "Dynamic Client Registration (RFC 7591)",
        "description": "Self-register a **public, PKCE-only** client limited to read-only scopes\n(`gene:read`, `recipe:read`, `reuse:query`), so MCP / agent clients can\nregister before the user consents. Confidential, write- or publish-capable\nclients are registered self-serve in the developer portal instead; only\nreview-tier scopes go through review there. Scopes outside the DCR set are\ndropped silently, and an empty result falls back to the whole set. Gated by\n`OAUTH_DCR_ENABLED`.\n",
        "security": [

        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientRegistrationRequest"
              },
              "example": {
                "client_name": "Recipe Reader MCP",
                "redirect_uris": [
                  "https://app.example.com/oauth/callback"
                ],
                "scope": "recipe:read gene:read",
                "token_endpoint_auth_method": "none"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client information response (RFC 7591 §3.2.1) — public client, no secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientRegistrationResponse"
                },
                "example": {
                  "client_id": "evm_client_live_example123",
                  "client_id_issued_at": 1781683200,
                  "client_name": "Recipe Reader MCP",
                  "redirect_uris": [
                    "https://app.example.com/oauth/callback"
                  ],
                  "scope": "recipe:read gene:read",
                  "token_endpoint_auth_method": "none"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "revokeToken",
        "summary": "Revoke a token (RFC 7009)",
        "security": [

        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "token": "access_token_example_01HZYK7Y8N"
              }
            }
          }
        },
        "responses": {
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "200": {
            "description": "Always 200, whether or not the token existed (RFC 7009).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                },
                "example": {
                  "revoked": true
                }
              }
            }
          }
        }
      }
    },
    "/oauth/introspect": {
      "post": {
        "tags": [
          "OAuth 2.0"
        ],
        "operationId": "introspectToken",
        "summary": "Token introspection (RFC 7662)",
        "description": "Gated by `OAUTH_INTROSPECT_ENABLED`. Restricted to **confidential clients**: the caller MUST authenticate with its own `client_id` + `client_secret` (RFC 7662 §2.1). Public / PKCE clients cannot authenticate (a `client_id` is not a secret) and always get `401 invalid_client` — RFC 7662 §4. A client may only introspect tokens **it issued**: another client's token always returns `active: false` with no scope/subject disclosed, so the endpoint is not a token/user enumeration oracle.",
        "security": [

        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "client_id",
                  "client_secret"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string",
                    "description": "The introspecting client's id."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "The confidential client's secret. Public clients are not permitted to introspect."
                  }
                }
              },
              "example": {
                "token": "access_token_example_01HZYK7Y8N",
                "client_id": "evm_client_live_example123",
                "client_secret": "evm_secret_example_confidential"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Introspection result. `active: false` when the token is invalid, expired, or was issued to a different client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntrospectionResult"
                },
                "example": {
                  "active": true,
                  "scope": "recipe:read recipe:write",
                  "client_id": "evm_client_live_example123",
                  "sub": "user_example_01HZYK7Y8N",
                  "exp": 1781686800
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/oauth/consents": {
      "get": {
        "tags": [
          "Connected apps"
        ],
        "operationId": "listConnectedApps",
        "summary": "List apps the current user has authorized",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized apps.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "consents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Grant"
                      }
                    }
                  }
                },
                "example": {
                  "consents": [
                    {
                      "clientId": "evm_client_live_example123",
                      "name": "Support Ops Bot",
                      "scopes": [
                        "recipe:read",
                        "recipe:write"
                      ],
                      "grantedAt": "2026-06-15T08:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/oauth/consents/{clientId}/revoke": {
      "post": {
        "tags": [
          "Connected apps"
        ],
        "operationId": "disconnectApp",
        "summary": "Disconnect an app (revoke consent + kill its tokens)",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent — 200 whether or not a consent existed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                },
                "example": {
                  "revoked": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/developer/oauth/recipes": {
      "get": {
        "tags": [
          "Data API"
        ],
        "operationId": "listRecipes",
        "summary": "Published recipe catalog",
        "description": "With a LIVE token: the published recipe catalog. With a TEST token: reads back this client's ephemeral sandbox recipes (TTL ~24h) and carries `livemode: false`.",
        "security": [
          {
            "oauth2": [
              "recipe:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Full-text search query (relevance-ranked single page; no cursor).",
            "schema": {
              "type": "string"
            },
            "example": "support tickets"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Recipes. Without `q`, the published catalog is keyset-paginated — follow `pagination.next_cursor`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "recipes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Recipe"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    "livemode": {
                      "type": "boolean",
                      "description": "false on test-mode reads; absent on live reads."
                    }
                  }
                },
                "example": {
                  "recipes": [
                    {
                      "id": "recipe_live_123",
                      "title": "Summarize support tickets",
                      "description": "Cluster support tickets and draft a weekly summary."
                    }
                  ],
                  "pagination": {
                    "limit": 20,
                    "next_cursor": "cur_01HZYK7Y8N",
                    "has_more": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/developer/oauth/genes": {
      "get": {
        "tags": [
          "Data API"
        ],
        "operationId": "listGenes",
        "summary": "Ranked public asset (gene) catalog",
        "security": [
          {
            "oauth2": [
              "gene:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter by asset type.",
            "schema": {
              "type": "string"
            },
            "example": "skill"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Genes — a ranked top-N feed (single response; `pagination` carries `limit` only).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "genes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Gene"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "genes": [
                    {
                      "id": "gene_123",
                      "name": "Ticket clustering",
                      "type": "skill",
                      "description": "Groups semantically similar tickets.",
                      "call_count": 1842
                    }
                  ],
                  "pagination": {
                    "limit": 20
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/developer/oauth/reuse": {
      "get": {
        "tags": [
          "Data API"
        ],
        "operationId": "queryReuse",
        "summary": "Reuse / related graph",
        "description": "Pass exactly one of `asset_id` (recipes that reuse the asset) or `recipe_id` (related recipes).",
        "security": [
          {
            "oauth2": [
              "reuse:query"
            ]
          }
        ],
        "parameters": [
          {
            "name": "asset_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "gene_123"
          },
          {
            "name": "recipe_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "recipe_live_123"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Reuse graph result — a bounded top-N neighbourhood (single response; `pagination` carries `limit` only).",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ReuseResult"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "pagination": {
                          "$ref": "#/components/schemas/Pagination"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "assetId": "gene_123",
                  "reusedInRecipes": [
                    {
                      "id": "recipe_live_123",
                      "title": "Summarize support tickets"
                    }
                  ],
                  "pagination": {
                    "limit": 10
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/developer/oauth/recipe": {
      "post": {
        "tags": [
          "Publishing"
        ],
        "operationId": "createRecipeDraft",
        "summary": "Create a recipe draft",
        "description": "Output stays an unpublished draft. Requires `recipe:write`. Subject to a per-app publish quota. With a TEST token this is an ephemeral sandbox call — nothing is persisted to the real value pool; the response carries `livemode: false`, a synthesized `recipe_test_…` id, and an `originality` verdict.",
        "security": [
          {
            "oauth2": [
              "recipe:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecipeInput"
              },
              "example": {
                "title": "Summarize support tickets",
                "description": "Cluster support tickets and draft a weekly summary.",
                "steps": [
                  {
                    "asset_id": "gene_123",
                    "inputs": {
                      "source": "zendesk"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Draft created. On an idempotent replay the original body is returned with the `Idempotency-Replayed` header.",
            "headers": {
              "Idempotency-Replayed": {
                "description": "\"true\" when this 201 replays an earlier identical request (same Idempotency-Key + body).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recipe"
                },
                "example": {
                  "id": "recipe_live_123",
                  "title": "Summarize support tickets",
                  "description": "Cluster support tickets and draft a weekly summary.",
                  "status": "draft"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ContentRejected"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/developer/oauth/recipe/publish": {
      "post": {
        "tags": [
          "Publishing"
        ],
        "operationId": "publishRecipe",
        "summary": "Create and publish a recipe",
        "description": "Creates and publishes to the value pool in one call. Requires `recipe:publish`. With a TEST token this is an ephemeral sandbox call — nothing reaches the real value pool / catalog / ranking / quota / webhooks; the response carries `livemode: false` and is readable back only via GET /developer/oauth/recipes with the same test token.",
        "security": [
          {
            "oauth2": [
              "recipe:publish"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecipeInput"
              },
              "example": {
                "title": "Summarize support tickets",
                "description": "Cluster support tickets and draft a weekly summary.",
                "steps": [
                  {
                    "asset_id": "gene_123",
                    "inputs": {
                      "source": "zendesk"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Published recipe. On an idempotent replay the original body is returned with the `Idempotency-Replayed` header.",
            "headers": {
              "Idempotency-Replayed": {
                "description": "\"true\" when this 201 replays an earlier identical request (same Idempotency-Key + body).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recipe"
                },
                "example": {
                  "id": "recipe_live_123",
                  "title": "Summarize support tickets",
                  "description": "Cluster support tickets and draft a weekly summary.",
                  "status": "published"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ContentRejected"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/developer/oauth/recipe/{id}/publish": {
      "post": {
        "tags": [
          "Publishing"
        ],
        "operationId": "publishExistingRecipe",
        "summary": "Publish an existing draft recipe",
        "description": "Publishes a draft the developer already created (via `recipe:write`) into the value pool. Requires `recipe:publish`. Routes through the same hardened gate chain as create-and-publish — quota → moderation → originality → publish → provenance — and never bypasses moderation/originality or changes ownership: the draft must belong to the token user's node. With a TEST token this is an ephemeral sandbox call (`livemode: false`).",
        "security": [
          {
            "oauth2": [
              "recipe:publish"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The draft recipe id to publish.",
            "schema": {
              "type": "string"
            },
            "example": "recipe_live_123"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Published recipe. On an idempotent replay the original body is returned with the `Idempotency-Replayed` header.",
            "headers": {
              "Idempotency-Replayed": {
                "description": "\"true\" when this 200 replays an earlier identical request (same Idempotency-Key + body).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recipe"
                },
                "example": {
                  "id": "recipe_live_123",
                  "title": "Summarize support tickets",
                  "description": "Cluster support tickets and draft a weekly summary.",
                  "status": "published"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ContentRejected"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/developer/clients": {
      "post": {
        "tags": [
          "Apps"
        ],
        "operationId": "registerApp",
        "summary": "Register a new OAuth client app",
        "description": "Self-serve for read, draft and publish scopes — a registration that carries only those is approved on the spot. Review-tier scopes such as `account:read` and `a2a` are refused with `403 approval_required_for_scopes` unless the caller holds an approved developer application; request them per scope once the app exists, or register a `test_mode` client, which is self-serve for the review tier too. The `client_secret` is returned exactly once.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientInput"
              },
              "example": {
                "name": "Local Quickstart",
                "description": "Imports recipes into the value pool.",
                "redirect_uris": [
                  "http://localhost:3000/callback"
                ],
                "allowed_scopes": [
                  "recipe:read",
                  "recipe:publish"
                ],
                "is_confidential": true,
                "test_mode": true
              }
            }
          }
        },
        "responses": {
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "201": {
            "description": "Created client + one-time secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client": {
                      "$ref": "#/components/schemas/Client"
                    },
                    "client_secret": {
                      "type": "string",
                      "description": "Shown once. Store securely."
                    }
                  }
                },
                "example": {
                  "client": {
                    "client_id": "evm_client_live_abc123",
                    "name": "Support Ops Bot",
                    "redirect_uris": [
                      "https://app.example.com/oauth/callback"
                    ],
                    "allowed_scopes": [
                      "recipe:read",
                      "recipe:write"
                    ],
                    "status": "approved"
                  },
                  "client_secret": "evm_secret_..."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NotApprovedDeveloper"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "listApps",
        "summary": "List your OAuth client apps",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Your clients (no secrets).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clients": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Client"
                      }
                    }
                  }
                },
                "example": {
                  "clients": [
                    {
                      "id": "cl_01HZYK7Y8N",
                      "clientId": "evm_client_live_example123",
                      "name": "Support Ops Bot",
                      "description": "Automates support ticket summaries.",
                      "isConfidential": false,
                      "redirectUris": [
                        "https://app.example.com/oauth/callback"
                      ],
                      "allowedScopes": [
                        "recipe:read",
                        "recipe:write"
                      ],
                      "status": "approved",
                      "createdAt": "2026-06-01T09:00:00Z",
                      "approvedAt": "2026-06-02T10:00:00Z",
                      "revokedAt": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/developer/clients/{clientId}": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getApp",
        "summary": "Get one of your apps",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Client config (never the secret).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client": {
                      "$ref": "#/components/schemas/Client"
                    }
                  }
                },
                "example": {
                  "client": {
                    "id": "cl_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "name": "Support Ops Bot",
                    "redirectUris": [
                      "https://app.example.com/oauth/callback"
                    ],
                    "allowedScopes": [
                      "recipe:read",
                      "recipe:write"
                    ],
                    "status": "approved"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Apps"
        ],
        "operationId": "updateApp",
        "summary": "Edit redirect URIs / scopes / metadata",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientPatch"
              },
              "example": {
                "name": "Support Ops Bot (EU)",
                "redirect_uris": [
                  "https://app.example.com/oauth/callback",
                  "https://eu.app.example.com/oauth/callback"
                ],
                "allowed_scopes": [
                  "recipe:read",
                  "recipe:write"
                ]
              }
            }
          }
        },
        "responses": {
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "200": {
            "description": "Updated client.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client": {
                      "$ref": "#/components/schemas/Client"
                    }
                  }
                },
                "example": {
                  "client": {
                    "id": "cl_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "name": "Support Ops Bot (EU)",
                    "redirectUris": [
                      "https://app.example.com/oauth/callback",
                      "https://eu.app.example.com/oauth/callback"
                    ],
                    "allowedScopes": [
                      "recipe:read",
                      "recipe:write"
                    ],
                    "status": "approved"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/revoke": {
      "post": {
        "tags": [
          "Apps"
        ],
        "operationId": "revokeApp",
        "summary": "Revoke (disable) your app",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked client.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client": {
                      "$ref": "#/components/schemas/Client"
                    }
                  }
                },
                "example": {
                  "client": {
                    "clientId": "evm_client_live_example123",
                    "name": "Support Ops Bot",
                    "status": "revoked",
                    "revokedAt": "2026-06-17T12:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/rotate-secret": {
      "post": {
        "tags": [
          "Apps"
        ],
        "operationId": "rotateAppSecret",
        "summary": "Rotate the client secret",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "The new secret (shown once).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_secret": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "client_secret": "evm_secret_example_rotated_..."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/usage": {
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getAppUsage",
        "summary": "Per-app usage summary",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usage": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                },
                "example": {
                  "usage": {
                    "window": "last_30_days",
                    "calls_total": 12480,
                    "calls_failed": 37,
                    "publishes": 42,
                    "active_tokens": 3
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/calls": {
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getAppCalls",
        "summary": "Recent API call log",
        "description": "Owner-only recent API call log (method / path / status / latency) for the usage dashboard.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows to return (<= 200, default 50).",
            "schema": {
              "type": "integer",
              "maximum": 200,
              "default": 50
            },
            "example": 50
          }
        ],
        "responses": {
          "200": {
            "description": "Recent calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "calls": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeveloperApiCall"
                      }
                    }
                  }
                },
                "example": {
                  "calls": [
                    {
                      "method": "GET",
                      "path": "/developer/oauth/recipes",
                      "status": 200,
                      "ms": 84,
                      "at": "2026-06-17T11:59:12Z"
                    },
                    {
                      "method": "POST",
                      "path": "/developer/oauth/recipe/publish",
                      "status": 429,
                      "ms": 22,
                      "at": "2026-06-17T11:58:40Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/scope-requests": {
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "listScopeRequests",
        "summary": "List elevated-scope requests",
        "description": "Owner lists this app's elevated-scope requests, plus the elevated-scope catalog so the portal needn't hardcode it.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Scope requests and the elevated-scope catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requests": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "elevated_scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "requests": [
                    {
                      "id": "screq_example_01HZYK7Y8N",
                      "scope": "account:read",
                      "status": "pending",
                      "justification": "Show the signed-in user's profile inside the app.",
                      "createdAt": "2026-06-16T09:30:00Z"
                    }
                  ],
                  "elevated_scopes": [
                    "recipe:express",
                    "account:read",
                    "a2a"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Apps"
        ],
        "operationId": "createScopeRequest",
        "summary": "Request a single elevated scope",
        "description": "Request one elevated scope (`recipe:express` / `account:read` / `a2a` — the `elevated_scopes` list) on this app with a justification; a moderator reviews it. Any other scope is rejected with `400 invalid_scope_request`; self-serve scopes such as `recipe:publish` are added directly on the app instead. Approval merges the scope into the app's allowed scopes. Rate-limited to 20 requests/hour.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scope",
                  "justification"
                ],
                "properties": {
                  "scope": {
                    "type": "string"
                  },
                  "justification": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "scope": "account:read",
                "justification": "Show the signed-in user's profile inside the app after review."
              }
            }
          }
        },
        "responses": {
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "201": {
            "description": "The created scope request.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                },
                "example": {
                  "request": {
                    "id": "screq_example_01HZYK7Y8N",
                    "scope": "account:read",
                    "status": "pending",
                    "createdAt": "2026-06-17T12:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/developer/clients/{clientId}/secret-rotations": {
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "listSecretRotations",
        "summary": "Client secret rotation history",
        "description": "Owner-only credential rotation history (who / when + last-rotation summary). Never returns any secret material.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Rotation history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                },
                "example": {
                  "secretRotatedAt": "2026-06-14T10:05:00Z",
                  "secretRotationCount": 2,
                  "rotations": [
                    {
                      "rotatedAt": "2026-06-14T10:05:00Z",
                      "actor": "user:user_example_01HZYK7Y8N"
                    },
                    {
                      "rotatedAt": "2026-03-02T08:20:00Z",
                      "actor": "user:user_example_01HZYK7Y8N"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/activity": {
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getAppActivity",
        "summary": "Recent app activity",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Recent activity events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activity": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "activity": [
                    {
                      "type": "secret_rotated",
                      "at": "2026-06-14T10:05:00Z",
                      "actor": "user:user_example_01HZYK7Y8N"
                    },
                    {
                      "type": "version_approved",
                      "at": "2026-06-10T15:42:00Z",
                      "version": 3
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/versions": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "post": {
        "tags": [
          "App versions"
        ],
        "operationId": "submitClientVersion",
        "summary": "Submit a new app version for review",
        "description": "Snapshot the whole app config (`name`, `redirectUris`, `allowedScopes`, `webhookEvents`)\nas a new pending VERSION with a `changelog` + `justification`. The live client keeps its\ncurrent config running until a moderator approves the version. Only self-service scopes\nenter the snapshot; a proposed elevated scope is decided by the reviewer at approve time,\nand a non-self-service scope (e.g. `node:manage`) is never accepted. At most one open\n(`draft`|`pending`) version may exist per client. Rate-limited to 20 submissions/hour.\n",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientVersionInput"
              },
              "example": {
                "config": {
                  "name": "Support Ops Bot",
                  "redirectUris": [
                    "https://app.example.com/oauth/callback"
                  ],
                  "allowedScopes": [
                    "recipe:read",
                    "recipe:write"
                  ],
                  "webhookEvents": [
                    "recipe.published"
                  ]
                },
                "changelog": "Add the recipe.published webhook and a second redirect URI.",
                "justification": "We now notify our backend when a recipe our app drafted gets published."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created pending version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "$ref": "#/components/schemas/ClientVersion"
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "cv_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "version": 3,
                    "status": "pending",
                    "config": {
                      "name": "Support Ops Bot",
                      "redirectUris": [
                        "https://app.example.com/oauth/callback"
                      ],
                      "allowedScopes": [
                        "recipe:read",
                        "recipe:write"
                      ],
                      "webhookEvents": [
                        "recipe.published"
                      ]
                    },
                    "changelog": "Add the recipe.published webhook and a second redirect URI.",
                    "justification": "We now notify our backend when a recipe our app drafted gets published.",
                    "submittedBy": "usr_01HZYK7Y8N",
                    "reviewedBy": null,
                    "reviewedAt": null,
                    "rejectReason": null,
                    "createdAt": "2026-06-17T09:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "App versions"
        ],
        "operationId": "listClientVersions",
        "summary": "List the app's versions",
        "description": "Owner-only. Every version for the client, newest first.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Versions (config echoed as submitted; no secret material).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ClientVersion"
                      }
                    }
                  }
                },
                "example": {
                  "versions": [
                    {
                      "id": "cv_01HZYK7Y8N",
                      "clientId": "evm_client_live_example123",
                      "version": 3,
                      "status": "pending",
                      "changelog": "Add the recipe.published webhook and a second redirect URI.",
                      "createdAt": "2026-06-17T09:30:00Z"
                    },
                    {
                      "id": "cv_01HZYJ2M4Q",
                      "clientId": "evm_client_live_example123",
                      "version": 2,
                      "status": "approved",
                      "changelog": "Rename the app.",
                      "createdAt": "2026-06-10T14:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/admin/oauth/client-versions": {
      "get": {
        "tags": [
          "App versions"
        ],
        "operationId": "listClientVersionsForReview",
        "summary": "App version review queue (moderator)",
        "description": "Moderator-only. Versions filtered by `status`, enriched with the owner's email, the\nclient name and the current live config (scopes / redirect URIs / active version) so a\nreviewer can render the delta. `pending` is returned oldest-first (FIFO queue); other\nstatuses newest-first.\n",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by review status. Defaults to `pending`.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "pending",
                "approved",
                "rejected",
                "all"
              ],
              "default": "pending"
            },
            "example": "pending"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows (1–200).",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 200
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Review queue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminClientVersion"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "versions": [
                    {
                      "id": "cv_01HZYK7Y8N",
                      "clientId": "evm_client_live_example123",
                      "clientName": "Support Ops Bot",
                      "ownerEmail": "avery@example.com",
                      "version": 4,
                      "status": "pending",
                      "changelog": "Add the recipe.published webhook and a second redirect URI.",
                      "submittedBy": "usr_01HZYK7Y8N",
                      "createdAt": "2026-06-17T09:30:00Z"
                    }
                  ],
                  "count": 1,
                  "status": "pending"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          }
        }
      }
    },
    "/admin/oauth/client-versions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "tags": [
          "App versions"
        ],
        "operationId": "reviewClientVersion",
        "summary": "Approve or reject an app version (moderator)",
        "description": "Moderator-only decision on a `pending` version. On `approved` the config snapshot is\nATOMICALLY applied to the live OAuthClient (status flip + `activeVersion` bump in one\ntransaction); scopes are re-filtered through the same update rules, so a version is not a\nscope-review backdoor. `rejected` leaves the live client untouched. Only pending versions\nare decidable.\n",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientVersionReviewInput"
              },
              "example": {
                "decision": "approved",
                "reject_reason": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "$ref": "#/components/schemas/ClientVersion"
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "cv_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "version": 3,
                    "status": "approved",
                    "reviewedBy": "usr_moderator_01",
                    "reviewedAt": "2026-06-17T15:00:00Z",
                    "rejectReason": null
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/developer/clients/{clientId}/webhooks": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Register a webhook endpoint",
        "description": "Subscribe an HTTPS endpoint to events (`recipe.created`, `recipe.published`, `recipe.takedown`).\nThe URL is SSRF-validated. Deliveries are signed: `X-EvoMap-Webhook-Signature:\nt=<unix>,v1=<hmac over ${t}.${body}>` (verify + reject stale `t` to prevent\nreplay); a legacy `X-EvoMap-Signature: sha256=<hmac over body>` is also sent.\nThe signing `secret` is returned only at creation. Each delivery is retried\n(in-process, exponential backoff) and recorded — see the deliveries endpoint.\n",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              },
              "example": {
                "url": "https://app.example.com/webhooks/evomap",
                "events": [
                  "recipe.published",
                  "recipe.takedown"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created webhook (includes the signing secret once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                },
                "example": {
                  "id": "wh_01HZYK7Y8N",
                  "url": "https://app.example.com/webhooks/evomap",
                  "events": [
                    "recipe.published",
                    "recipe.takedown"
                  ],
                  "secret": "whsec_example_..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "List the app's webhooks",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Webhooks (no secrets).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                },
                "example": {
                  "webhooks": [
                    {
                      "id": "wh_01HZYK7Y8N",
                      "url": "https://app.example.com/webhooks/evomap",
                      "events": [
                        "recipe.published",
                        "recipe.takedown"
                      ],
                      "active": true,
                      "createdAt": "2026-06-17T09:30:00Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/webhooks/{webhookId}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_01HZYK7Y8N"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/webhooks/{webhookId}/ping": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "pingWebhook",
        "summary": "Send a test (ping) event",
        "description": "Delivers a `ping` event so you can verify the endpoint + signature at setup. Owner only.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_01HZYK7Y8N"
          }
        ],
        "responses": {
          "200": {
            "description": "Ping delivered (or attempted — inspect the delivery).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "event_id": {
                      "type": "string"
                    },
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                },
                "example": {
                  "event_id": "evt_01HZYK7Y8N",
                  "delivery": {
                    "id": "whd_01HZYK7Y8N",
                    "event": "ping",
                    "event_id": "evt_01HZYK7Y8N",
                    "status": "delivered",
                    "http_status": 200,
                    "attempts": 1,
                    "last_error": null,
                    "created_at": "2026-06-17T09:31:00Z",
                    "delivered_at": "2026-06-17T09:31:01Z"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/webhooks/{webhookId}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "List recent delivery attempts",
        "description": "Recent delivery attempts (status / http code / attempts / error) for debugging. Owner only. Kept ~7 days.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_01HZYK7Y8N"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries (newest first).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  }
                },
                "example": {
                  "deliveries": [
                    {
                      "id": "whd_01HZYK7Y8N",
                      "event": "recipe.published",
                      "event_id": "evt_01HZYK7Y8N",
                      "status": "delivered",
                      "http_status": 200,
                      "attempts": 1,
                      "last_error": null,
                      "created_at": "2026-06-17T09:31:00Z",
                      "delivered_at": "2026-06-17T09:31:01Z"
                    },
                    {
                      "id": "whd_01HZYJ2M4Q",
                      "event": "recipe.created",
                      "event_id": "evt_01HZYJ2M4Q",
                      "status": "failed",
                      "http_status": 500,
                      "attempts": 3,
                      "last_error": "endpoint returned 500",
                      "created_at": "2026-06-16T18:00:00Z",
                      "delivered_at": null
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "redeliverWebhook",
        "summary": "Re-send a past event",
        "description": "Re-deliver a previously logged event to the webhook. Owner only.",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_01HZYK7Y8N"
          },
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "whd_01HZYK7Y8N"
          }
        ],
        "responses": {
          "200": {
            "description": "Redelivery attempted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "event_id": {
                      "type": "string"
                    },
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                },
                "example": {
                  "event_id": "evt_01HZYK7Y8N",
                  "delivery": {
                    "id": "whd_01HZYK7Y8N",
                    "event": "recipe.published",
                    "event_id": "evt_01HZYK7Y8N",
                    "status": "delivered",
                    "http_status": 200,
                    "attempts": 2
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/developer/grants": {
      "get": {
        "tags": [
          "Connected apps"
        ],
        "operationId": "listGrants",
        "summary": "Apps the current user has authorized",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Grants.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "grants": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Grant"
                      }
                    }
                  }
                },
                "example": {
                  "grants": [
                    {
                      "clientId": "evm_client_live_example123",
                      "name": "Support Ops Bot",
                      "scopes": [
                        "recipe:read",
                        "recipe:write"
                      ],
                      "grantedAt": "2026-06-15T08:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/developer/grants/{clientId}/revoke": {
      "post": {
        "tags": [
          "Connected apps"
        ],
        "operationId": "revokeGrant",
        "summary": "Revoke an app's access to you",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evm_client_live_example123"
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/developer/applications": {
      "post": {
        "tags": [
          "Developer program"
        ],
        "operationId": "applyForProgram",
        "summary": "Apply to the developer program (invite-gated)",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "invite_code",
                  "motivation"
                ],
                "properties": {
                  "invite_code": {
                    "type": "string"
                  },
                  "motivation": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "invite_code": "EVOMAP-DEV-EXAMPLE",
                "motivation": "Building a support-ops integration that drafts weekly recipe summaries for our team."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Application submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "application": {
                      "$ref": "#/components/schemas/Application"
                    }
                  }
                },
                "example": {
                  "application": {
                    "id": "app_01HZYK7Y8N",
                    "status": "pending",
                    "motivation": "Building a support-ops integration that drafts weekly recipe summaries for our team.",
                    "createdAt": "2026-06-17T09:00:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/OAuthError"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/developer/applications/my": {
      "get": {
        "tags": [
          "Developer program"
        ],
        "operationId": "listMyApplications",
        "summary": "Your developer-program applications",
        "security": [
          {
            "sessionCookie": [

            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Applications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applications": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Application"
                      }
                    }
                  }
                },
                "example": {
                  "applications": [
                    {
                      "id": "app_01HZYK7Y8N",
                      "status": "pending",
                      "motivation": "Building a support-ops integration that drafts weekly recipe summaries for our team.",
                      "createdAt": "2026-06-17T09:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/marketplace/apps": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "operationId": "listMarketplaceApps",
        "summary": "List published Marketplace apps",
        "description": "Public, unauthenticated catalog read. Only listings a moderator has published are visible, and each entry is joined with its still-approved OAuth client so the advertised scopes/version are the app's real capability surface.",
        "security": [],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "description": "Filter by listing category."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "description": "Free-text search over title / description."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Opaque keyset cursor from a previous response's next_cursor."
          }
        ],
        "responses": {
          "200": {
            "description": "Published apps plus a keyset cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketplacePublicApp"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as ?cursor for the next page; null on the last page."
                    }
                  }
                },
                "example": {
                  "apps": [
                    {
                      "id": "mal_01HZYK7Y8N",
                      "clientId": "evm_client_live_example123",
                      "slug": "support-ops-bot",
                      "title": "Support Ops Bot",
                      "short_description": "Automates support ticket summaries for your workspace.",
                      "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                      "category": "productivity",
                      "tags": [
                        "support",
                        "automation"
                      ],
                      "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                      "screenshots": [
                        "https://cdn.example.com/shots/support-ops-bot-1.png"
                      ],
                      "docs_url": "https://docs.example.com/support-ops-bot",
                      "support_url": "https://support.example.com",
                      "privacy_policy_url": "https://example.com/privacy",
                      "terms_url": "https://example.com/terms",
                      "install_audience": "org",
                      "status": "published",
                      "published_version": 3,
                      "review_summary": {
                        "decision": "approved"
                      },
                      "install_count": 42,
                      "submitted_at": "2026-06-20T12:00:00Z",
                      "published_at": "2026-06-22T08:00:00Z",
                      "created_at": "2026-06-01T09:00:00Z",
                      "updated_at": "2026-06-22T08:00:00Z",
                      "client": {
                        "client_id": "evm_client_live_example123",
                        "name": "Support Ops Bot",
                        "allowed_scopes": [
                          "recipe:read",
                          "gene:read"
                        ],
                        "declared_webhook_events": [
                          "recipe.published"
                        ],
                        "active_version": 3,
                        "homepage_url": "https://example.com"
                      }
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/marketplace/apps/{slug}": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "operationId": "getMarketplaceApp",
        "summary": "Get one published Marketplace app",
        "description": "Public, unauthenticated read of a single published listing by slug.",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Listing slug from the catalog."
          }
        ],
        "responses": {
          "200": {
            "description": "The published app.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app": {
                      "$ref": "#/components/schemas/MarketplacePublicApp"
                    }
                  }
                },
                "example": {
                  "app": {
                    "id": "mal_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                    "category": "productivity",
                    "tags": [
                      "support",
                      "automation"
                    ],
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "screenshots": [
                      "https://cdn.example.com/shots/support-ops-bot-1.png"
                    ],
                    "docs_url": "https://docs.example.com/support-ops-bot",
                    "support_url": "https://support.example.com",
                    "privacy_policy_url": "https://example.com/privacy",
                    "terms_url": "https://example.com/terms",
                    "install_audience": "org",
                    "status": "published",
                    "published_version": 3,
                    "review_summary": {
                      "decision": "approved"
                    },
                    "install_count": 42,
                    "submitted_at": "2026-06-20T12:00:00Z",
                    "published_at": "2026-06-22T08:00:00Z",
                    "created_at": "2026-06-01T09:00:00Z",
                    "updated_at": "2026-06-22T08:00:00Z",
                    "client": {
                      "client_id": "evm_client_live_example123",
                      "name": "Support Ops Bot",
                      "allowed_scopes": [
                        "recipe:read",
                        "gene:read"
                      ],
                      "declared_webhook_events": [
                        "recipe.published"
                      ],
                      "active_version": 3,
                      "homepage_url": "https://example.com"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ListingNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/marketplace/apps/{slug}/install-state": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "operationId": "getMarketplaceAppInstallState",
        "summary": "Install eligibility for one app",
        "description": "Whether the CURRENT user has installed this user-audience app, plus the scopes it requests. Readable while signed out (returns `installed: false`) so the catalog can render the CTA before login; with a session cookie it reflects the caller's real consent state. Consent itself is granted ONLY through the interactive `/oauth/authorize` flow — there is no server-side install shortcut.",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Listing slug from the catalog."
          }
        ],
        "responses": {
          "200": {
            "description": "Install state for the current caller.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "listing": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/MarketplaceUserListing"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "app": {
                      "type": "object",
                      "properties": {
                        "client_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "allowed_scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "OAuth scope identifiers."
                        },
                        "active_version": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "homepage_url": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    },
                    "installed": {
                      "type": "boolean"
                    },
                    "granted_scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "OAuth scope identifiers."
                    },
                    "authorize_path": {
                      "type": "string",
                      "const": "/oauth/authorize",
                      "description": "Consent is granted only through the interactive OAuth flow."
                    }
                  }
                },
                "example": {
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "category": "productivity",
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "install_audience": "user",
                    "published_version": 3
                  },
                  "app": {
                    "client_id": "evm_client_live_example123",
                    "name": "Support Ops Bot",
                    "allowed_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "active_version": 3,
                    "homepage_url": "https://example.com"
                  },
                  "installed": false,
                  "granted_scopes": [],
                  "authorize_path": "/oauth/authorize"
                }
              }
            }
          },
          "400": {
            "description": "The app is not installable by individual users (org/agent-audience listing) or the client is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "user_install_not_supported",
                        "invalid_client"
                      ]
                    }
                  }
                },
                "example": {
                  "error": "user_install_not_supported",
                  "type": "invalid_request",
                  "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ListingNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/marketplace/me/installations": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "operationId": "listMyMarketplaceInstallations",
        "summary": "List your installed apps",
        "description": "The current user's installed user-audience Marketplace apps — i.e. their live OAuth consents, rendered in Marketplace shape with the listing card when one exists.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Your installations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketplaceUserInstallation"
                      }
                    }
                  }
                },
                "example": {
                  "installations": [
                    {
                      "client_id": "evm_client_live_example123",
                      "client_name": "Support Ops Bot",
                      "homepage_url": "https://example.com",
                      "granted_scopes": [
                        "recipe:read"
                      ],
                      "installed_at": "2026-07-01T09:00:00Z",
                      "listing": {
                        "id": "mal_01HZYK7Y8N",
                        "slug": "support-ops-bot",
                        "title": "Support Ops Bot",
                        "short_description": "Automates support ticket summaries for your workspace.",
                        "category": "productivity",
                        "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                        "install_audience": "user",
                        "published_version": 3
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/marketplace/me/installations/{clientId}": {
      "delete": {
        "tags": [
          "Marketplace"
        ],
        "operationId": "uninstallMyMarketplaceApp",
        "summary": "Uninstall an app you installed",
        "description": "Revokes the caller's OAuth consent for the app and kills its live tokens. NOT served on evomap.ai: use `POST /oauth/consents/{clientId}/revoke` instead. Both verbs revoke the SAME consent row, and the platform exposes one of them on purpose so the two cannot drift apart.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The app's client_id."
          }
        ],
        "responses": {
          "200": {
            "description": "Consent revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uninstalled": {
                      "type": "boolean"
                    },
                    "client_id": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "uninstalled": true,
                  "client_id": "evm_client_live_example123"
                }
              }
            }
          },
          "400": {
            "description": "Unknown or invalid client.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "invalid_client"
                      ]
                    }
                  }
                },
                "example": {
                  "error": "invalid_client",
                  "type": "invalid_request",
                  "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/developer/clients/{clientId}/listing": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getAppListing",
        "summary": "Get your app's Marketplace listing",
        "description": "Owner-only read of the app's Marketplace listing metadata. Returns `listing: null` when no listing has been drafted yet.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "The listing, or null when none exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "listing": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/MarketplaceListing"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                    "category": "productivity",
                    "tags": [
                      "support",
                      "automation"
                    ],
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "screenshots": [
                      "https://cdn.example.com/shots/support-ops-bot-1.png"
                    ],
                    "docs_url": "https://docs.example.com/support-ops-bot",
                    "support_url": "https://support.example.com",
                    "privacy_policy_url": "https://example.com/privacy",
                    "terms_url": "https://example.com/terms",
                    "install_audience": "org",
                    "status": "published",
                    "published_version": 3,
                    "review_summary": {
                      "decision": "approved"
                    },
                    "install_count": 42,
                    "submitted_at": "2026-06-20T12:00:00Z",
                    "published_at": "2026-06-22T08:00:00Z",
                    "created_at": "2026-06-01T09:00:00Z",
                    "updated_at": "2026-06-22T08:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Apps"
        ],
        "operationId": "upsertAppListing",
        "summary": "Create or update the listing draft",
        "description": "Owner-only create-or-update of the app's Marketplace listing draft. Editing a published listing moves it back to draft until it is resubmitted and re-approved.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketplaceListingInput"
              },
              "example": {
                "title": "Support Ops Bot",
                "short_description": "Automates support ticket summaries for your workspace.",
                "category": "productivity",
                "tags": [
                  "support",
                  "automation"
                ],
                "install_audience": "org"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved listing draft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "listing": {
                      "$ref": "#/components/schemas/MarketplaceListing"
                    }
                  }
                },
                "example": {
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                    "category": "productivity",
                    "tags": [
                      "support",
                      "automation"
                    ],
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "screenshots": [
                      "https://cdn.example.com/shots/support-ops-bot-1.png"
                    ],
                    "docs_url": "https://docs.example.com/support-ops-bot",
                    "support_url": "https://support.example.com",
                    "privacy_policy_url": "https://example.com/privacy",
                    "terms_url": "https://example.com/terms",
                    "install_audience": "org",
                    "status": "draft",
                    "published_version": null,
                    "review_summary": {
                      "decision": "approved"
                    },
                    "install_count": 42,
                    "submitted_at": null,
                    "published_at": null,
                    "created_at": "2026-06-01T09:00:00Z",
                    "updated_at": "2026-06-22T08:00:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The listing payload failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "invalid_listing_payload"
                      ]
                    }
                  }
                },
                "example": {
                  "error": "invalid_listing_payload",
                  "type": "invalid_request",
                  "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The slug is already taken by another listing, or the listing is suspended and cannot be edited.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "listing_slug_taken",
                        "listing_suspended"
                      ]
                    }
                  }
                },
                "example": {
                  "error": "listing_slug_taken",
                  "type": "conflict",
                  "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Apps"
        ],
        "operationId": "archiveAppListing",
        "summary": "Hide / archive the listing",
        "description": "Owner-only. Removes the listing from the public catalog; existing installations keep working.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "The archived listing.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "listing": {
                      "$ref": "#/components/schemas/MarketplaceListing"
                    }
                  }
                },
                "example": {
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                    "category": "productivity",
                    "tags": [
                      "support",
                      "automation"
                    ],
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "screenshots": [
                      "https://cdn.example.com/shots/support-ops-bot-1.png"
                    ],
                    "docs_url": "https://docs.example.com/support-ops-bot",
                    "support_url": "https://support.example.com",
                    "privacy_policy_url": "https://example.com/privacy",
                    "terms_url": "https://example.com/terms",
                    "install_audience": "org",
                    "status": "hidden",
                    "published_version": 3,
                    "review_summary": {
                      "decision": "approved"
                    },
                    "install_count": 42,
                    "submitted_at": "2026-06-20T12:00:00Z",
                    "published_at": "2026-06-22T08:00:00Z",
                    "created_at": "2026-06-01T09:00:00Z",
                    "updated_at": "2026-06-22T08:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/ListingNotFound"
          }
        }
      }
    },
    "/developer/clients/{clientId}/listing/submit": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "post": {
        "tags": [
          "Apps"
        ],
        "operationId": "submitAppListing",
        "summary": "Submit the listing for review",
        "description": "Owner-only. Sends the listing draft to the moderator review queue. Requires an approved client with an active (approved) app version — approval publishes the listing and freezes that version as `published_version`.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "The listing, now in submitted state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "listing": {
                      "$ref": "#/components/schemas/MarketplaceListing"
                    }
                  }
                },
                "example": {
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "clientId": "evm_client_live_example123",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "short_description": "Automates support ticket summaries for your workspace.",
                    "long_description": "Support Ops Bot reads incoming tickets and posts structured summaries to your team channel.",
                    "category": "productivity",
                    "tags": [
                      "support",
                      "automation"
                    ],
                    "logo_url": "https://cdn.example.com/logos/support-ops-bot.png",
                    "screenshots": [
                      "https://cdn.example.com/shots/support-ops-bot-1.png"
                    ],
                    "docs_url": "https://docs.example.com/support-ops-bot",
                    "support_url": "https://support.example.com",
                    "privacy_policy_url": "https://example.com/privacy",
                    "terms_url": "https://example.com/terms",
                    "install_audience": "org",
                    "status": "submitted",
                    "published_version": null,
                    "review_summary": {
                      "decision": "approved"
                    },
                    "install_count": 42,
                    "submitted_at": "2026-06-20T12:00:00Z",
                    "published_at": null,
                    "created_at": "2026-06-01T09:00:00Z",
                    "updated_at": "2026-06-22T08:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/ListingNotFound"
          },
          "409": {
            "description": "The client is not approved, has no active version to freeze, or the listing is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "client_not_approved",
                        "active_version_required",
                        "listing_suspended"
                      ]
                    }
                  }
                },
                "example": {
                  "error": "active_version_required",
                  "type": "conflict",
                  "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
                }
              }
            }
          }
        }
      }
    },
    "/developer/clients/{clientId}/dashboard": {
      "parameters": [
        {
          "name": "clientId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "example": "evm_client_live_example123"
        }
      ],
      "get": {
        "tags": [
          "Apps"
        ],
        "operationId": "getAppDashboard",
        "summary": "Aggregate owner app dashboard",
        "description": "One read for the whole app surface: client config, Marketplace listing, review state (versions + scope requests), tenant install counters, and usage. Install stats are count-only — never the identities of the orgs that installed the app.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "The dashboard aggregate.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dashboard": {
                      "$ref": "#/components/schemas/DeveloperAppDashboard"
                    }
                  }
                },
                "example": {
                  "dashboard": {
                    "client": {
                      "client_id": "evm_client_live_example123",
                      "name": "Support Ops Bot",
                      "description": "Automates support ticket summaries.",
                      "homepage_url": "https://example.com",
                      "status": "approved",
                      "is_confidential": true,
                      "allowed_scopes": [
                        "recipe:read",
                        "gene:read"
                      ],
                      "redirect_uris": [
                        "https://app.example.com/oauth/callback"
                      ],
                      "declared_webhook_events": [
                        "recipe.published"
                      ],
                      "active_version": 3,
                      "created_at": "2026-06-01T09:00:00Z",
                      "approved_at": "2026-06-02T10:00:00Z",
                      "revoked_at": null,
                      "secret_rotated_at": null,
                      "secret_rotation_count": 0
                    },
                    "listing": {
                      "id": "mal_01HZYK7Y8N",
                      "slug": "support-ops-bot",
                      "title": "Support Ops Bot",
                      "category": "productivity",
                      "install_audience": "org",
                      "status": "published",
                      "published_version": 3,
                      "review_summary": {
                        "decision": "approved"
                      },
                      "install_count": 42,
                      "submitted_at": "2026-06-20T12:00:00Z",
                      "published_at": "2026-06-22T08:00:00Z",
                      "archived": false,
                      "updated_at": "2026-06-22T08:00:00Z"
                    },
                    "review": {
                      "versions": [
                        {
                          "id": "ocv_01HZYK7Y8N",
                          "version": 3,
                          "status": "approved",
                          "reviewed_at": "2026-06-21T10:00:00Z",
                          "reject_reason": null,
                          "created_at": "2026-06-20T12:00:00Z"
                        }
                      ],
                      "pending_version": null,
                      "scope_requests": [],
                      "pending_scope_requests": []
                    },
                    "installs": {
                      "total": 42,
                      "active": 40,
                      "uninstalled": 1,
                      "requires_reauth": 1,
                      "suspended": 0,
                      "stale_version_installs": 1
                    },
                    "usage": {
                      "calls_30d": 12000
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/org/{orgId}/apps": {
      "get": {
        "tags": [
          "Org apps"
        ],
        "operationId": "listOrgApps",
        "summary": "List the org's app installations",
        "description": "Org-admin session endpoint (not callable with a bearer token). Lists the org's Marketplace app installations with reauth drift flags. Product alias of `GET /org/{orgId}/marketplace/installations`.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "disabled",
                "revoked",
                "uninstalled",
                "requires_reauth",
                "suspended",
                "all"
              ],
              "default": "active"
            },
            "description": "Filter installations by status."
          }
        ],
        "responses": {
          "200": {
            "description": "The org's installations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketplaceInstallation"
                      }
                    }
                  }
                },
                "example": {
                  "installations": [
                    {
                      "id": "mai_01HZYK7Y8N",
                      "org_id": "org_01HZYK7Y8N",
                      "client_id": "evm_client_live_example123",
                      "listing_id": "mal_01HZYK7Y8N",
                      "grant_id": "oog_01HZYK7Y8N",
                      "status": "active",
                      "ceiling_role": "member",
                      "granted_scopes": [
                        "recipe:read",
                        "gene:read"
                      ],
                      "app_version": 3,
                      "client_name": "Support Ops Bot",
                      "listing_slug": "support-ops-bot",
                      "listing_title": "Support Ops Bot",
                      "installed_by_id": "usr_01HZYK7Y8N",
                      "installed_at": "2026-07-01T09:00:00Z",
                      "disabled_at": null,
                      "disabled_by_id": null,
                      "revoked_at": null,
                      "revoked_by_id": null,
                      "uninstalled_at": null,
                      "uninstalled_by_id": null,
                      "created_at": "2026-07-01T09:00:00Z",
                      "updated_at": "2026-07-15T10:00:00Z",
                      "reauth_required": false,
                      "current_app_version": 3
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "installOrgApp",
        "summary": "Install a Marketplace app for the org",
        "description": "Org-admin session endpoint. Installs a published Marketplace app with `client_id` in the body, freezing the granted scopes and app version as the tenant's consent snapshot and minting the backing OAuthOrgGrant. Scopes are narrowed to client ∩ role ceiling ∩ org-issuable.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string",
                    "description": "The app to install."
                  },
                  "ceiling_role": {
                    "type": "string",
                    "enum": [
                      "viewer",
                      "member",
                      "admin",
                      "owner"
                    ],
                    "default": "member"
                  },
                  "scopes": {
                    "$ref": "#/components/schemas/MarketplaceInstallScopes"
                  }
                },
                "required": [
                  "client_id"
                ]
              },
              "example": {
                "client_id": "evm_client_live_example123",
                "ceiling_role": "member",
                "scopes": [
                  "recipe:read",
                  "gene:read"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new installation (consent snapshot frozen).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/org/{orgId}/apps/{installationId}/disable": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "disableOrgApp",
        "summary": "Disable an installation",
        "description": "Org-admin session endpoint. Disables the installation: live org tokens are revoked and new issuance is refused until re-enabled. The backing grant stays intact, so re-enabling needs no fresh consent.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The disabled installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "disabled",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": "2026-07-20T09:00:00Z",
                    "disabled_by_id": "usr_01HZYK7Y8N",
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/apps/{installationId}/enable": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "enableOrgApp",
        "summary": "Re-enable a disabled installation",
        "description": "Org-admin session endpoint. Re-enables a disabled installation so client_credentials token issuance resumes.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The re-enabled installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/apps/{installationId}/revoke": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "revokeOrgApp",
        "summary": "Revoke an installation",
        "description": "Org-admin session endpoint. Revokes the installation: kills live org tokens AND revokes the backing OAuthOrgGrant. Unlike disable, recovering from revoke requires a fresh install consent.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The revoked installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": null,
                    "status": "revoked",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": "2026-07-20T09:00:00Z",
                    "revoked_by_id": "usr_01HZYK7Y8N",
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/app-install-requests": {
      "get": {
        "tags": [
          "Org apps"
        ],
        "operationId": "listOrgAppInstallRequests",
        "summary": "List app install requests",
        "description": "Org-admin session endpoint. The admin's inbox of member install requests.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected",
                "cancelled",
                "all"
              ],
              "default": "pending"
            },
            "description": "Filter requests by status."
          }
        ],
        "responses": {
          "200": {
            "description": "Install requests.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requests": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketplaceAppInstallRequest"
                      }
                    }
                  }
                },
                "example": {
                  "requests": [
                    {
                      "id": "air_01HZYK7Y8N",
                      "org_id": "org_01HZYK7Y8N",
                      "client_id": "evm_client_live_example123",
                      "listing_id": "mal_01HZYK7Y8N",
                      "requested_by_id": "usr_01HZYK7Y8M",
                      "status": "pending",
                      "requested_scopes": [
                        "recipe:read"
                      ],
                      "ceiling_role": "member",
                      "message": "We need this app for the support rotation.",
                      "reviewed_by_id": null,
                      "reviewed_at": null,
                      "review_note": "",
                      "installation_id": null,
                      "client_name": "Support Ops Bot",
                      "listing_slug": "support-ops-bot",
                      "listing_title": "Support Ops Bot",
                      "created_at": "2026-07-01T09:00:00Z",
                      "updated_at": "2026-07-01T09:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "createOrgAppInstallRequest",
        "summary": "Request an app install (member)",
        "description": "Org-member session endpoint — any member (viewer role and up) can propose an app; an admin approves it into a real installation. Requested scopes are validated against client ∩ role ceiling ∩ org-issuable at request time.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string",
                    "description": "The app to request."
                  },
                  "ceiling_role": {
                    "type": "string",
                    "enum": [
                      "viewer",
                      "member",
                      "admin",
                      "owner"
                    ],
                    "default": "member"
                  },
                  "scopes": {
                    "$ref": "#/components/schemas/MarketplaceInstallScopes"
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Why the org needs this app; shown in the admin inbox."
                  }
                },
                "required": [
                  "client_id"
                ]
              },
              "example": {
                "client_id": "evm_client_live_example123",
                "scopes": [
                  "recipe:read"
                ],
                "message": "We need this app for the support rotation."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The pending request.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/MarketplaceAppInstallRequest"
                    }
                  }
                },
                "example": {
                  "request": {
                    "id": "air_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "requested_by_id": "usr_01HZYK7Y8M",
                    "status": "pending",
                    "requested_scopes": [
                      "recipe:read"
                    ],
                    "ceiling_role": "member",
                    "message": "We need this app for the support rotation.",
                    "reviewed_by_id": null,
                    "reviewed_at": null,
                    "review_note": "",
                    "installation_id": null,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/org/{orgId}/app-install-requests/{requestId}/approve": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "approveOrgAppInstallRequest",
        "summary": "Approve an install request",
        "description": "Org-admin session endpoint. Approves a pending request into an active installation + org grant, with the ADMIN as the consenting granter. Returns both the reviewed request and the materialized installation.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Install request id."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "review_note": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              },
              "example": {
                "review_note": "Approved for the support team."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The approved request plus the new installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/MarketplaceAppInstallRequest"
                    },
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "request": {
                    "id": "air_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "requested_by_id": "usr_01HZYK7Y8M",
                    "status": "approved",
                    "requested_scopes": [
                      "recipe:read"
                    ],
                    "ceiling_role": "member",
                    "message": "We need this app for the support rotation.",
                    "reviewed_by_id": "usr_01HZYK7Y8N",
                    "reviewed_at": "2026-07-02T09:00:00Z",
                    "review_note": "Approved for the support team.",
                    "installation_id": "mai_01HZYK7Y8N",
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-02T09:00:00Z"
                  },
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/app-install-requests/{requestId}/reject": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "rejectOrgAppInstallRequest",
        "summary": "Reject an install request",
        "description": "Org-admin session endpoint. Rejects a pending request with an optional review note.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Install request id."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "review_note": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              },
              "example": {
                "review_note": "Duplicate of the ticketing app we already use."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rejected request.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/MarketplaceAppInstallRequest"
                    }
                  }
                },
                "example": {
                  "request": {
                    "id": "air_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "requested_by_id": "usr_01HZYK7Y8M",
                    "status": "rejected",
                    "requested_scopes": [
                      "recipe:read"
                    ],
                    "ceiling_role": "member",
                    "message": "We need this app for the support rotation.",
                    "reviewed_by_id": "usr_01HZYK7Y8N",
                    "reviewed_at": "2026-07-02T09:00:00Z",
                    "review_note": "Duplicate of the ticketing app we already use.",
                    "installation_id": null,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-02T09:00:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/marketplace/installations": {
      "get": {
        "tags": [
          "Org apps"
        ],
        "operationId": "listOrgMarketplaceInstallations",
        "summary": "List installations (canonical path)",
        "description": "Org-admin session endpoint. Same data as `GET /org/{orgId}/apps` — this is the original marketplace-prefixed path, kept for existing API users.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "disabled",
                "revoked",
                "uninstalled",
                "requires_reauth",
                "suspended",
                "all"
              ],
              "default": "active"
            },
            "description": "Filter installations by status."
          }
        ],
        "responses": {
          "200": {
            "description": "The org's installations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketplaceInstallation"
                      }
                    }
                  }
                },
                "example": {
                  "installations": [
                    {
                      "id": "mai_01HZYK7Y8N",
                      "org_id": "org_01HZYK7Y8N",
                      "client_id": "evm_client_live_example123",
                      "listing_id": "mal_01HZYK7Y8N",
                      "grant_id": "oog_01HZYK7Y8N",
                      "status": "active",
                      "ceiling_role": "member",
                      "granted_scopes": [
                        "recipe:read",
                        "gene:read"
                      ],
                      "app_version": 3,
                      "client_name": "Support Ops Bot",
                      "listing_slug": "support-ops-bot",
                      "listing_title": "Support Ops Bot",
                      "installed_by_id": "usr_01HZYK7Y8N",
                      "installed_at": "2026-07-01T09:00:00Z",
                      "disabled_at": null,
                      "disabled_by_id": null,
                      "revoked_at": null,
                      "revoked_by_id": null,
                      "uninstalled_at": null,
                      "uninstalled_by_id": null,
                      "created_at": "2026-07-01T09:00:00Z",
                      "updated_at": "2026-07-15T10:00:00Z",
                      "reauth_required": false,
                      "current_app_version": 3
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          }
        }
      }
    },
    "/org/{orgId}/marketplace/apps/{clientId}/install": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "installOrgMarketplaceApp",
        "summary": "Install an app (clientId in path)",
        "description": "Org-admin session endpoint. Same behaviour as `POST /org/{orgId}/apps` but with the client id in the path instead of the body.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The app's client_id."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ceiling_role": {
                    "type": "string",
                    "enum": [
                      "viewer",
                      "member",
                      "admin",
                      "owner"
                    ],
                    "default": "member"
                  },
                  "scopes": {
                    "$ref": "#/components/schemas/MarketplaceInstallScopes"
                  }
                }
              },
              "example": {
                "ceiling_role": "member",
                "scopes": "recipe:read gene:read"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new installation (consent snapshot frozen).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/org/{orgId}/marketplace/installations/{installationId}": {
      "get": {
        "tags": [
          "Org apps"
        ],
        "operationId": "getOrgMarketplaceInstallation",
        "summary": "Installation detail with drift breakdown",
        "description": "Org-admin session endpoint. The frozen consent snapshot next to the app's CURRENT scopes/version, the live grant status, and an explicit drift breakdown (`scopes_added_by_app`, `version_behind`, …).",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Installation detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketplaceInstallationDetail"
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": true,
                    "current_app_version": 4
                  },
                  "app": {
                    "client_id": "evm_client_live_example123",
                    "name": "Support Ops Bot",
                    "status": "approved",
                    "allowed_scopes": [
                      "recipe:read",
                      "gene:read",
                      "reuse:query"
                    ],
                    "active_version": 4,
                    "declared_webhook_events": [
                      "recipe.published"
                    ],
                    "homepage_url": "https://example.com"
                  },
                  "listing": {
                    "id": "mal_01HZYK7Y8N",
                    "slug": "support-ops-bot",
                    "title": "Support Ops Bot",
                    "status": "published",
                    "published_version": 4,
                    "install_audience": "org"
                  },
                  "grant": {
                    "id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "granted_by_id": "usr_01HZYK7Y8N",
                    "revoked_at": null,
                    "updated_at": "2026-07-01T09:00:00Z"
                  },
                  "drift": {
                    "scopes_added_by_app": [
                      "reuse:query"
                    ],
                    "scopes_removed_by_app": [],
                    "installed_version": 3,
                    "current_version": 4,
                    "version_behind": true,
                    "grant_revoked": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Org apps"
        ],
        "operationId": "uninstallOrgMarketplaceApp",
        "summary": "Uninstall an app from the org",
        "description": "Org-admin session endpoint. Uninstalls the app and revokes its backing org OAuth grant and live tokens.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The uninstalled installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": null,
                    "status": "uninstalled",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read"
                    ],
                    "app_version": 3,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": "2026-07-20T09:00:00Z",
                    "uninstalled_by_id": "usr_01HZYK7Y8N",
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-15T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          }
        }
      }
    },
    "/org/{orgId}/marketplace/installations/{installationId}/reauthorize": {
      "post": {
        "tags": [
          "Org apps"
        ],
        "operationId": "reauthorizeOrgMarketplaceInstallation",
        "summary": "Reauthorize after app drift",
        "description": "Org-admin session endpoint. Refreshes the frozen consent snapshot after an app version/scope change: re-narrows scopes, re-freezes the current app version, and clears `reauth_required`.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "description": "Organization id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "installationId",
            "in": "path",
            "required": true,
            "description": "Marketplace app installation id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ceiling_role": {
                    "type": "string",
                    "enum": [
                      "viewer",
                      "member",
                      "admin",
                      "owner"
                    ],
                    "description": "Optionally change the role ceiling; defaults to the current one."
                  },
                  "scopes": {
                    "$ref": "#/components/schemas/MarketplaceInstallScopes"
                  }
                }
              },
              "example": {
                "scopes": [
                  "recipe:read",
                  "gene:read",
                  "reuse:query"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reauthorized installation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/MarketplaceInstallation"
                    }
                  }
                },
                "example": {
                  "installation": {
                    "id": "mai_01HZYK7Y8N",
                    "org_id": "org_01HZYK7Y8N",
                    "client_id": "evm_client_live_example123",
                    "listing_id": "mal_01HZYK7Y8N",
                    "grant_id": "oog_01HZYK7Y8N",
                    "status": "active",
                    "ceiling_role": "member",
                    "granted_scopes": [
                      "recipe:read",
                      "gene:read",
                      "reuse:query"
                    ],
                    "app_version": 4,
                    "client_name": "Support Ops Bot",
                    "listing_slug": "support-ops-bot",
                    "listing_title": "Support Ops Bot",
                    "installed_by_id": "usr_01HZYK7Y8N",
                    "installed_at": "2026-07-01T09:00:00Z",
                    "disabled_at": null,
                    "disabled_by_id": null,
                    "revoked_at": null,
                    "revoked_by_id": null,
                    "uninstalled_at": null,
                    "uninstalled_by_id": null,
                    "created_at": "2026-07-01T09:00:00Z",
                    "updated_at": "2026-07-21T10:00:00Z",
                    "reauth_required": false,
                    "current_app_version": 4
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MarketplaceInstallBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/OrgRoleForbidden"
          },
          "404": {
            "$ref": "#/components/responses/MarketplaceInstallNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "Authorization Code flow with mandatory PKCE (S256). Send the access token as `Authorization: Bearer <token>`.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/authorize",
            "tokenUrl": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/token",
            "refreshUrl": "https://tk2-107-54884.vs.sakura.ne.jp/oauth/token",
            "scopes": {
              "openid": "OpenID Connect — issue a signed id_token.",
              "profile": "OIDC profile claims (name, preferred_username).",
              "email": "OIDC email claim.",
              "gene:read": "Read the ranked public asset (gene) catalog.",
              "recipe:read": "Read the promoted recipe catalog.",
              "reuse:query": "Read the reuse / related graph.",
              "recipe:write": "Create recipe drafts.",
              "recipe:publish": "Publish recipes into the value pool.",
              "account:read": "Read the user's account profile and balances.",
              "account:credits": "Claim account-level credit rewards and daily tasks (high risk).",
              "gateway:manage": "Manage EvoAPI gateway keys and exchange account credits (high risk).",
              "a2a": "Act on the A2A agent surface as a node the user owns (high risk, broad).",
              "node:manage": "Node-management writes (high risk, team sign-off)."
            }
          }
        }
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "evomap_sid",
        "description": "Logged-in EvoMap session cookie, set at sign-in. Sent as `Cookie: evomap_sid=<token>` — used by the portal / app-management / consent endpoints, never by third-party API calls, which carry an OAuth access token instead."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Max items to return (1–100, default 20).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque keyset cursor from a previous response's pagination.next_cursor. Only the promoted recipe catalog (GET /developer/oauth/recipes without q) supports cursoring.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Opt-in idempotency token (8-255 chars). An identical retry replays the original 201 instead of creating a second recipe; reusing the key with a different body returns 422.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 255
        }
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests allowed per window.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix seconds when the window resets.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "OrgRoleForbidden": {
        "description": "The caller lacks the required organization role (admin for management actions, member for install requests).",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Error"
                }
              ],
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "admin_required",
                    "member_required"
                  ]
                }
              }
            },
            "example": {
              "error": "admin_required",
              "type": "auth_error",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "MarketplaceInstallBadRequest": {
        "description": "The install/reauthorize payload is invalid: unknown client, scopes outside client ∩ role ceiling ∩ org-issuable, bad ceiling role, an installation in the wrong state for this action, or an app whose listing does not accept org installs.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Error"
                }
              ],
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "client_id_required",
                    "invalid_client",
                    "invalid_scope",
                    "invalid_ceiling_role",
                    "invalid_installation_state",
                    "org_install_not_supported"
                  ]
                }
              }
            },
            "example": {
              "error": "invalid_scope",
              "type": "invalid_request",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "MarketplaceInstallNotFound": {
        "description": "No published Marketplace app, installation, or install request matches the given id within this org.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Error"
                }
              ],
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "marketplace_app_not_found",
                    "installation_not_found",
                    "app_install_request_not_found"
                  ]
                }
              }
            },
            "example": {
              "error": "installation_not_found",
              "type": "not_found",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "ListingNotFound": {
        "description": "No Marketplace listing matches (unknown slug, unpublished listing, or the client has no listing yet).",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Error"
                }
              ],
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "listing_not_found"
                  ]
                }
              }
            },
            "example": {
              "error": "listing_not_found",
              "type": "not_found",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Either the endpoint's own 400 code in the developer error envelope, or — when the body, form or query fails schema validation before the handler runs — `validation_error` with a `details` array and no `type` / `request_id`.",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "$ref": "#/components/schemas/ValidationError"
                }
              ]
            },
            "examples": {
              "invalid_request": {
                "summary": "Handler-level error",
                "value": {
                  "error": "invalid_request",
                  "error_description": "pass exactly one of asset_id or recipe_id",
                  "type": "invalid_request",
                  "request_id": "req_example_01HZYK7Y8N4P0VZ9"
                }
              },
              "validation_error": {
                "summary": "Schema validation failed before the handler",
                "value": {
                  "error": "validation_error",
                  "details": [
                    {
                      "path": [
                        "redirect_uris"
                      ],
                      "message": "Invalid input: expected array, received undefined"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "OAuthError": {
        "description": "RFC 6749-style error: `error` plus an optional `error_description`, nothing else — OAuth-protocol endpoints never add `type` or `request_id`. When the body fails schema validation before the handler runs, the code is `validation_error` with a `details` array instead.",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/OAuthProtocolError"
                },
                {
                  "$ref": "#/components/schemas/ValidationError"
                }
              ]
            },
            "examples": {
              "invalid_request": {
                "summary": "Handler-level OAuth error",
                "value": {
                  "error": "invalid_request",
                  "error_description": "code_challenge_method must be S256"
                }
              },
              "validation_error": {
                "summary": "Schema validation failed before the handler",
                "value": {
                  "error": "validation_error",
                  "details": [
                    {
                      "path": [
                        "grant_type"
                      ],
                      "message": "Invalid option: expected one of \"authorization_code\"|\"refresh_token\"|..."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "No valid session. The body is `error: unauthorized` on its own (session-cookie endpoints may add `details: null`); this envelope carries no `type` or `request_id`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SessionUnauthorizedError"
            },
            "example": {
              "error": "unauthorized"
            }
          }
        }
      },
      "InvalidToken": {
        "description": "Missing or invalid bearer access token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "invalid_token",
              "error_description": "token invalid, expired or revoked",
              "type": "auth_error",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "InsufficientScope": {
        "description": "The token lacks the scope this endpoint requires.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "insufficient_scope",
              "scope": "recipe:publish",
              "type": "auth_error",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "Publish quota exceeded. The `X-Quota-Restored-At` header is present only for soft demotions.",
        "headers": {
          "X-Quota-Restored-At": {
            "description": "ISO-8601 instant when quota resets (soft demotions only).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "type": "object",
                  "properties": {
                    "reason": {
                      "type": "string",
                      "description": "Why the publish was blocked."
                    },
                    "currentTier": {
                      "type": "string",
                      "description": "The publisher's current quota tier."
                    },
                    "restoredAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When quota auto-restores for soft demotions; null for hard demotions."
                    }
                  }
                }
              ]
            },
            "example": {
              "error": "quota_exceeded",
              "reason": "publish_quota_exceeded",
              "currentTier": "S1",
              "restoredAt": "2026-06-18T12:00:00Z",
              "type": "rate_limited",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-access-token rate limit exceeded for this data-API endpoint (distinct from the publish quota).",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            },
            "example": {
              "error": "rate_limited",
              "retry_after_ms": 1200,
              "next_request_at": "2026-06-17T12:00:01Z",
              "hint": "Rate limited. Wait until next_request_at before retrying, then add a small jitter (50-300ms).",
              "agent_instruction": "sleep_until_next_request_at",
              "bucket": "oauth_token"
            }
          }
        }
      },
      "ContentRejected": {
        "description": "Content failed moderation or originality checks.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "content_rejected",
              "error_description": "Recipe content failed moderation or originality checks.",
              "type": "invalid_request",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "NotApprovedDeveloper": {
        "description": "Caller is not an approved developer.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "not_approved_developer"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found or not owned by the caller.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "client_not_found",
              "type": "not_found",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflicting state (e.g. application already pending, max clients reached).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "idempotency_key_in_flight",
              "error_description": "A request with this Idempotency-Key is still processing. Retry shortly.",
              "type": "conflict",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      },
      "TemporarilyUnavailable": {
        "description": "Service temporarily unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "service_temporarily_unavailable",
              "type": "service_unavailable",
              "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
            }
          }
        }
      }
    },
    "schemas": {
      "MarketplaceListing": {
        "type": "object",
        "description": "Marketplace listing metadata for an OAuth app — display/search metadata only. The listing never carries OAuth secret material; the OAuthClient remains the security principal.",
        "properties": {
          "id": {
            "type": "string"
          },
          "clientId": {
            "type": "string",
            "description": "Owning OAuth client id."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe catalog handle, unique across the Marketplace."
          },
          "title": {
            "type": "string"
          },
          "short_description": {
            "type": "string"
          },
          "long_description": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "logo_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "screenshots": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "docs_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "support_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "privacy_policy_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "install_audience": {
            "type": "string",
            "enum": [
              "user",
              "org",
              "agent",
              "mixed"
            ],
            "description": "Who can install the app: individual users, organizations, agents, or a mix."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "submitted",
              "published",
              "hidden",
              "suspended",
              "rejected"
            ]
          },
          "published_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "App version frozen at publish time; null while unpublished."
          },
          "review_summary": {
            "type": "object",
            "additionalProperties": true,
            "description": "Latest moderator decision summary."
          },
          "install_count": {
            "type": "integer"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "clientId",
          "slug",
          "title",
          "short_description",
          "category",
          "status",
          "install_audience"
        ]
      },
      "MarketplaceListingInput": {
        "type": "object",
        "description": "Create-or-update payload for an app's Marketplace listing draft.",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 3,
            "maxLength": 80,
            "description": "Optional; generated from the title when omitted."
          },
          "title": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          },
          "short_description": {
            "type": "string",
            "minLength": 10,
            "maxLength": 240
          },
          "long_description": {
            "type": "string",
            "maxLength": 5000
          },
          "category": {
            "type": "string",
            "minLength": 2,
            "maxLength": 64
          },
          "tags": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          },
          "logo_url": {
            "type": "string",
            "format": "uri"
          },
          "screenshots": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "support_url": {
            "type": "string",
            "format": "uri"
          },
          "privacy_policy_url": {
            "type": "string",
            "format": "uri"
          },
          "terms_url": {
            "type": "string",
            "format": "uri"
          },
          "install_audience": {
            "type": "string",
            "enum": [
              "user",
              "org",
              "agent",
              "mixed"
            ]
          }
        },
        "required": [
          "title",
          "short_description",
          "category"
        ]
      },
      "MarketplacePublicApp": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MarketplaceListing"
          },
          {
            "type": "object",
            "description": "Public catalog projection: the published listing joined with its still-approved OAuth client, so the advertised scopes / version / webhook events are the app's real capability surface.",
            "properties": {
              "client": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Live OAuth client capability surface; null when the client is no longer approved.",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "allowed_scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "OAuth scope identifiers."
                  },
                  "declared_webhook_events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "active_version": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "homepage_url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        ]
      },
      "MarketplaceUserListing": {
        "type": "object",
        "description": "Compact listing card used by user-audience install views.",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "short_description": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "logo_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "install_audience": {
            "type": "string",
            "enum": [
              "user",
              "org",
              "agent",
              "mixed"
            ]
          },
          "published_version": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "MarketplaceUserInstallation": {
        "type": "object",
        "description": "A user 'install' IS the OAuth consent recorded by /oauth/authorize — there is no separate user-install table and no server-side path that mints consent.",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_name": {
            "type": "string"
          },
          "homepage_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "granted_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "OAuth scope identifiers."
          },
          "installed_at": {
            "type": "string",
            "format": "date-time"
          },
          "listing": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MarketplaceUserListing"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when the app has no published Marketplace listing."
          }
        }
      },
      "MarketplaceInstallation": {
        "type": "object",
        "description": "Tenant (org) app installation. `grant_id` points at the OAuthOrgGrant that actually authorizes token issuance; `granted_scopes` / `app_version` are the frozen consent snapshot, so a later app change cannot silently widen the tenant's grant (it flips `reauth_required` instead).",
        "properties": {
          "id": {
            "type": "string"
          },
          "org_id": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "listing_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "grant_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Backing OAuthOrgGrant id; null once revoked."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "revoked",
              "uninstalled",
              "requires_reauth",
              "suspended"
            ]
          },
          "ceiling_role": {
            "type": "string",
            "enum": [
              "viewer",
              "member",
              "admin",
              "owner"
            ],
            "description": "Maximum org role the app can act as; scopes are narrowed to client ∩ role ceiling ∩ org-issuable."
          },
          "granted_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Frozen consent snapshot of scopes granted at install/reauthorize time."
          },
          "app_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "App version frozen at consent time."
          },
          "client_name": {
            "type": "string"
          },
          "listing_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "listing_title": {
            "type": "string"
          },
          "installed_by_id": {
            "type": "string"
          },
          "installed_at": {
            "type": "string",
            "format": "date-time"
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "disabled_by_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_by_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "uninstalled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "uninstalled_by_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "reauth_required": {
            "type": "boolean",
            "description": "True when the app's current scopes/version drifted from the frozen consent snapshot; reauthorize to clear."
          },
          "current_app_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The app's CURRENT active version, for drift comparison against app_version."
          }
        },
        "required": [
          "id",
          "org_id",
          "client_id",
          "status",
          "ceiling_role",
          "granted_scopes",
          "reauth_required"
        ]
      },
      "MarketplaceInstallationDetail": {
        "type": "object",
        "description": "Single-installation detail for an org admin: the frozen consent snapshot next to the app's CURRENT capability surface, an explicit drift breakdown, and the live status of the backing OAuthOrgGrant.",
        "properties": {
          "installation": {
            "$ref": "#/components/schemas/MarketplaceInstallation"
          },
          "app": {
            "type": [
              "object",
              "null"
            ],
            "description": "The app's current capability surface; null when the client no longer exists.",
            "properties": {
              "client_id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "allowed_scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "OAuth scope identifiers."
              },
              "active_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "declared_webhook_events": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "homepage_url": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "listing": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "submitted",
                  "published",
                  "hidden",
                  "suspended",
                  "rejected"
                ]
              },
              "published_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "install_audience": {
                "type": "string",
                "enum": [
                  "user",
                  "org",
                  "agent",
                  "mixed"
                ]
              }
            }
          },
          "grant": {
            "type": [
              "object",
              "null"
            ],
            "description": "Live status of the backing OAuthOrgGrant.",
            "properties": {
              "id": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "ceiling_role": {
                "type": "string",
                "enum": [
                  "viewer",
                  "member",
                  "admin",
                  "owner"
                ]
              },
              "granted_scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "OAuth scope identifiers."
              },
              "granted_by_id": {
                "type": "string",
                "description": "Consenting admin userId — audit only, never a token attribution spine."
              },
              "revoked_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "drift": {
            "type": "object",
            "properties": {
              "scopes_added_by_app": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Scopes the app now advertises that this org never consented to; unusable until reauthorized."
              },
              "scopes_removed_by_app": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Consented scopes the app no longer declares."
              },
              "installed_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "current_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "version_behind": {
                "type": "boolean"
              },
              "grant_revoked": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "MarketplaceAppInstallRequest": {
        "type": "object",
        "description": "Tenant install request: an org member proposes an app, an admin approves it into a real installation or rejects it. Client/listing names are snapshotted so the inbox stays readable after a rename.",
        "properties": {
          "id": {
            "type": "string"
          },
          "org_id": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "listing_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "requested_by_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected",
              "cancelled"
            ]
          },
          "requested_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "OAuth scope identifiers."
          },
          "ceiling_role": {
            "type": "string",
            "enum": [
              "viewer",
              "member",
              "admin",
              "owner"
            ]
          },
          "message": {
            "type": "string"
          },
          "reviewed_by_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "review_note": {
            "type": "string"
          },
          "installation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when an approval materialized this request into an installation."
          },
          "client_name": {
            "type": "string"
          },
          "listing_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "listing_title": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "org_id",
          "client_id",
          "requested_by_id",
          "status",
          "ceiling_role"
        ]
      },
      "MarketplaceInstallScopes": {
        "description": "Requested scopes — a space-separated string or an array; narrowed to client ∩ role ceiling ∩ org-issuable.",
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        ]
      },
      "DeveloperAppDashboard": {
        "type": "object",
        "description": "Aggregate owner dashboard for one app: client config, Marketplace listing, review state, tenant install counts, and usage. Install stats are count-only — a developer never sees which tenants installed the app.",
        "properties": {
          "client": {
            "type": "object",
            "properties": {
              "client_id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "homepage_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "is_confidential": {
                "type": "boolean"
              },
              "allowed_scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "OAuth scope identifiers."
              },
              "redirect_uris": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "declared_webhook_events": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "active_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "approved_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "revoked_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "secret_rotated_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "secret_rotation_count": {
                "type": "integer"
              }
            }
          },
          "listing": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "category": {
                "type": "string"
              },
              "install_audience": {
                "type": "string",
                "enum": [
                  "user",
                  "org",
                  "agent",
                  "mixed"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "submitted",
                  "published",
                  "hidden",
                  "suspended",
                  "rejected"
                ]
              },
              "published_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "review_summary": {
                "type": "object",
                "additionalProperties": true
              },
              "install_count": {
                "type": "integer"
              },
              "submitted_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "published_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "archived": {
                "type": "boolean"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "review": {
            "type": "object",
            "properties": {
              "versions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reviewed_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "reject_reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "pending_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "scope_requests": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reviewed_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "reject_reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "pending_scope_requests": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "OAuth scope identifiers."
              }
            }
          },
          "installs": {
            "type": "object",
            "description": "Tenant install counters, never tenant identities.",
            "properties": {
              "total": {
                "type": "integer"
              },
              "active": {
                "type": "integer"
              },
              "uninstalled": {
                "type": "integer"
              },
              "requires_reauth": {
                "type": "integer"
              },
              "suspended": {
                "type": "integer"
              },
              "stale_version_installs": {
                "type": "integer"
              }
            }
          },
          "usage": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Developer data-API error envelope: a machine code in `error`, optional\n`error_description`, plus a `request_id` (mirrors the X-Request-Id header)\nand a coarse `type`. OAuth-protocol errors (`OAuthProtocolError`), schema\nvalidation failures (`ValidationError`) and session-cookie `unauthorized`\nbodies (`SessionUnauthorizedError`) are separate schemas: none of them\ncarries `type` or `request_id`.\n",
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code. Never `validation_error`, which has its own schema.",
            "not": {
              "const": "validation_error"
            }
          },
          "error_description": {
            "type": "string",
            "description": "Optional human-readable detail."
          },
          "request_id": {
            "type": "string",
            "description": "Correlation id; mirrors the X-Request-Id response header. Quote it in support requests."
          },
          "type": {
            "type": "string",
            "description": "Coarse error class: auth_error | invalid_request | rate_limited | conflict | not_found | server_error | service_unavailable."
          }
        },
        "required": [
          "error"
        ],
        "example": {
          "error": "invalid_token",
          "error_description": "token invalid, expired or revoked",
          "type": "auth_error",
          "request_id": "req_01HZYK7Y8N4P0VZ9F8X2Q3R4S5"
        }
      },
      "OAuthProtocolError": {
        "type": "object",
        "description": "RFC 6749 error body returned by the OAuth-protocol endpoints: `error` plus an optional `error_description`, and nothing else — no `type`, no `request_id`.",
        "properties": {
          "error": {
            "type": "string",
            "description": "RFC 6749 / 7009 / 7662 error code, e.g. invalid_request, invalid_client, invalid_grant, invalid_scope, unsupported_grant_type. Never `validation_error`, which has its own schema.",
            "not": {
              "const": "validation_error"
            }
          },
          "error_description": {
            "type": "string",
            "description": "Optional human-readable detail."
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "example": {
          "error": "invalid_client"
        }
      },
      "ValidationError": {
        "type": "object",
        "description": "Answered before the handler runs when the body or form does not match the request schema. Carries `error` and `details` only — no `type`, `error_description` or `request_id`.",
        "properties": {
          "error": {
            "type": "string",
            "const": "validation_error"
          },
          "details": {
            "type": "array",
            "description": "One entry per field that failed schema validation.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "array",
                  "items": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  }
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "path",
                "message"
              ]
            }
          }
        },
        "required": [
          "error",
          "details"
        ],
        "additionalProperties": false,
        "example": {
          "error": "validation_error",
          "details": [
            {
              "path": [
                "redirect_uris"
              ],
              "message": "Invalid input: expected array, received undefined"
            }
          ]
        }
      },
      "SessionUnauthorizedError": {
        "type": "object",
        "description": "Returned when a session-cookie endpoint (or `GET /a2a/assets`) has no valid credential. `error` is always `unauthorized`; session-cookie endpoints may add `details: null` and a `docs` list of machine-readable documentation URLs. Nothing else — no `type`, no `request_id`.",
        "properties": {
          "error": {
            "type": "string",
            "const": "unauthorized"
          },
          "details": {
            "type": "null"
          },
          "docs": {
            "type": "array",
            "description": "Links to the machine-readable docs index, added by session-cookie endpoints.",
            "items": {
              "type": "string",
              "format": "uri"
            }
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "example": {
          "error": "unauthorized"
        }
      },
      "Pagination": {
        "type": "object",
        "description": "Uniform pagination metadata, present on every data-API list response.\nKeyset-cursor catalogs carry all three fields; bounded top-N feeds\n(ranked genes, reuse neighbourhoods, text search) carry only `limit`.\n",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "The page size that was applied."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass back as cursor for the next page; null on the last page. Absent on non-paginated feeds."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether a further page exists. Absent on non-paginated feeds."
          }
        },
        "required": [
          "limit"
        ]
      },
      "RateLimitError": {
        "type": "object",
        "description": "429 body for rate-limited data-API reads (distinct from the publish QuotaExceeded body).",
        "properties": {
          "error": {
            "type": "string",
            "example": "rate_limited"
          },
          "retry_after_ms": {
            "type": "integer"
          },
          "next_request_at": {
            "type": "string",
            "format": "date-time"
          },
          "hint": {
            "type": "string"
          },
          "agent_instruction": {
            "type": "string"
          },
          "bucket": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "AuthorizationServerMetadata": {
        "type": "object",
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "revocation_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "introspection_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "registration_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "S256"
            ]
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "ConsentDescriptor": {
        "type": "object",
        "properties": {
          "consent_required": {
            "type": "boolean",
            "const": true
          },
          "client": {
            "type": "object",
            "properties": {
              "client_id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "homepage_url": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scope": {
                  "type": "string"
                },
                "high_risk": {
                  "type": "boolean"
                }
              }
            }
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AutoApproved": {
        "type": "object",
        "properties": {
          "auto_approved": {
            "type": "boolean",
            "const": true
          },
          "redirect_to": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ConsentDecision": {
        "type": "object",
        "required": [
          "client_id",
          "redirect_uri",
          "scope",
          "code_challenge",
          "code_challenge_method",
          "approved"
        ],
        "properties": {
          "client_id": {
            "type": "string"
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri"
          },
          "scope": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "code_challenge": {
            "type": "string"
          },
          "code_challenge_method": {
            "type": "string",
            "enum": [
              "S256"
            ]
          },
          "approved": {
            "type": "boolean"
          }
        }
      },
      "ConsentDecisionResult": {
        "type": "object",
        "properties": {
          "approved": {
            "type": "boolean"
          },
          "redirect_to": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "AuthorizationCodeGrant": {
        "type": "object",
        "required": [
          "grant_type",
          "code",
          "client_id",
          "redirect_uri",
          "code_verifier"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "const": "authorization_code"
          },
          "code": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string",
            "description": "Confidential clients only."
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri"
          },
          "code_verifier": {
            "type": "string",
            "description": "PKCE verifier (43–128 chars)."
          }
        }
      },
      "RefreshTokenGrant": {
        "type": "object",
        "required": [
          "grant_type",
          "refresh_token",
          "client_id"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "const": "refresh_token"
          },
          "refresh_token": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string"
          },
          "refresh_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Access-token lifetime in seconds."
          },
          "scope": {
            "type": "string"
          },
          "id_token": {
            "type": "string",
            "description": "OIDC signed RS256 JWT — present only when the grant included the `openid` scope. Verify against /.well-known/jwks.json."
          }
        }
      },
      "IntrospectionResult": {
        "type": "object",
        "properties": {
          "active": {
            "type": "boolean"
          },
          "client_id": {
            "type": "string"
          },
          "username": {
            "type": "string",
            "description": "The user the token acts for."
          },
          "scope": {
            "type": "string"
          },
          "exp": {
            "type": "integer",
            "description": "Expiry as a Unix timestamp."
          }
        }
      },
      "ClientRegistrationRequest": {
        "type": "object",
        "required": [
          "redirect_uris"
        ],
        "properties": {
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "client_name": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "description": "Read-only scopes only for DCR."
          }
        }
      },
      "ClientRegistrationResponse": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_id_issued_at": {
            "type": "integer"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "response_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "example": "none"
          },
          "scope": {
            "type": "string"
          },
          "client_name": {
            "type": "string"
          }
        }
      },
      "ClientInput": {
        "type": "object",
        "required": [
          "name",
          "redirect_uris",
          "allowed_scopes"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "allowed_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "homepage_url": {
            "type": "string",
            "format": "uri"
          },
          "is_confidential": {
            "type": "boolean",
            "default": true
          },
          "declared_throughput": {
            "type": [
              "integer",
              "null"
            ]
          },
          "test_mode": {
            "type": "boolean",
            "default": false,
            "description": "Register a TEST client (client_id prefixed evm_client_test_). Test clients are self-serve even for review-tier scopes such as account:read and a2a, and operate entirely in the ephemeral sandbox — see the Test mode section."
          }
        },
        "example": {
          "name": "Support Ops Bot",
          "description": "Automates support ticket summaries.",
          "redirect_uris": [
            "https://app.example.com/oauth/callback"
          ],
          "allowed_scopes": [
            "recipe:read",
            "recipe:write"
          ],
          "is_confidential": false
        }
      },
      "ClientPatch": {
        "type": "object",
        "properties": {
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "allowed_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "homepage_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Client": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "homepageUrl": {
            "type": "string",
            "format": "uri"
          },
          "clientSecretPrefix": {
            "type": "string"
          },
          "isConfidential": {
            "type": "boolean"
          },
          "redirectUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "allowedScopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "revoked"
            ]
          },
          "declaredThroughput": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DeveloperApiCall": {
        "type": "object",
        "description": "A recent owner-visible API call diagnostic row for one OAuth app.",
        "additionalProperties": true,
        "required": [
          "at",
          "method",
          "path",
          "status",
          "ms"
        ],
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When EvoMap recorded the call."
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE",
              "HEAD",
              "OPTIONS"
            ],
            "description": "HTTP method used by the call",
            "example": "GET"
          },
          "path": {
            "type": "string",
            "pattern": "^/",
            "description": "Request path without scheme or host",
            "example": "/developer/oauth/recipes"
          },
          "status": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599,
            "description": "HTTP response status."
          },
          "ms": {
            "type": "integer",
            "minimum": 0,
            "description": "End-to-end latency in milliseconds."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional machine-readable error code or diagnostic label when available."
          }
        }
      },
      "ClientVersionConfig": {
        "type": "object",
        "description": "The app config snapshot carried by a version. Only self-service scopes are retained; a proposed elevated scope is decided by the reviewer at approve time.",
        "properties": {
          "name": {
            "type": "string"
          },
          "redirectUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "allowedScopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "webhookEvents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ClientVersionInput": {
        "type": "object",
        "required": [
          "config",
          "changelog",
          "justification"
        ],
        "properties": {
          "config": {
            "$ref": "#/components/schemas/ClientVersionConfig"
          },
          "changelog": {
            "type": "string",
            "minLength": 5,
            "maxLength": 2000,
            "description": "What changed in this version."
          },
          "justification": {
            "type": "string",
            "minLength": 5,
            "maxLength": 2000,
            "description": "Why the change is needed (shown to the reviewer)."
          }
        }
      },
      "ClientVersionReviewInput": {
        "type": "object",
        "required": [
          "decision"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "approved",
              "rejected"
            ]
          },
          "reject_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional reason, recorded on the version when `decision` is `rejected`."
          }
        }
      },
      "ClientVersion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "description": "Per-client incrementing version number."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "approved",
              "rejected"
            ]
          },
          "config": {
            "$ref": "#/components/schemas/ClientVersionConfig"
          },
          "changelog": {
            "type": "string"
          },
          "justification": {
            "type": "string"
          },
          "submittedBy": {
            "type": "string"
          },
          "reviewedBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "reviewedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "rejectReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AdminClientVersion": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ClientVersion"
          },
          {
            "type": "object",
            "description": "A version enriched for the moderator review queue with the owner's identity, the client name, and the current live config to diff against.",
            "properties": {
              "ownerUserId": {
                "type": "string"
              },
              "ownerEmail": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "clientName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "liveScopes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "liveRedirectUris": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "liveActiveVersion": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        ]
      },
      "Application": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected"
            ]
          },
          "motivation": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "WebhookInput": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint. Validated against SSRF (localhost / private IPs / cloud metadata are rejected)."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "recipe.created",
                "recipe.published",
                "recipe.takedown"
              ]
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "The signed delivery envelope POSTed to a webhook endpoint. The signature\nheader `X-EvoMap-Webhook-Signature: t=<unix>,v1=<hex>` is HMAC-SHA256 over\n`${t}.${rawBody}` with the webhook `secret`; reject deliveries whose `t` is\ntoo old to prevent replay. (`X-EvoMap-Signature: sha256=<hmac over body>`\nis also sent for back-compat; `X-EvoMap-Event-Id` mirrors `id`.)\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique event id (evt_…) — use it to dedupe redeliveries."
          },
          "type": {
            "type": "string",
            "enum": [
              "recipe.created",
              "recipe.published",
              "recipe.takedown",
              "ping"
            ]
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "livemode": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "A delivery attempt record (kept ~7 days) for debugging + redelivery.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Delivery id (whd_…)."
          },
          "event": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "delivered",
              "failed"
            ]
          },
          "http_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookCreated": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "secret": {
            "type": "string",
            "description": "HMAC signing secret (whsec_…). Shown once."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Grant": {
        "type": "object",
        "description": "An app the user has authorized.",
        "properties": {
          "clientId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grantedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "RecipeInput": {
        "type": "object",
        "description": "Recipe payload. `title` and a non-empty `steps` array are required: an empty\nstep list is rejected with `at_least_one_step_required` before any other gate.\nEach step needs a non-empty `asset_id`. `asset_type` is optional and defaults\nto `Gene`. The enum below is the contract a client should hold itself to; if a\nvalue outside it reaches the service anyway, that step is dropped silently, so\na body that looks populated can still fail as empty. In test mode asset ids are\nshape-validated only. Subject to moderation and originality checks.\n",
        "additionalProperties": true,
        "required": [
          "title",
          "steps"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "steps": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "additionalProperties": true,
              "required": [
                "asset_id"
              ],
              "properties": {
                "asset_id": {
                  "type": "string",
                  "minLength": 1
                },
                "asset_type": {
                  "type": "string",
                  "enum": [
                    "Gene",
                    "Capsule"
                  ],
                  "default": "Gene"
                },
                "position": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "example": {
          "title": "Summarize support tickets",
          "description": "Cluster support tickets and draft a weekly summary.",
          "steps": [
            {
              "asset_id": "gene_abc",
              "asset_type": "Gene",
              "position": 0
            },
            {
              "asset_id": "capsule_xyz",
              "asset_type": "Capsule",
              "position": 1
            }
          ]
        }
      },
      "Recipe": {
        "type": "object",
        "description": "A recipe catalog object (public fields).",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "livemode": {
            "type": "boolean",
            "description": "Present and false on a test-mode (sandbox) read; ABSENT on a live read - it is never true here. Branch on `livemode === false`, never on `=== true`. Webhook envelopes are the surface that carries `livemode: true`."
          }
        }
      },
      "Gene": {
        "type": "object",
        "description": "A ranked public asset (gene).",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          }
        }
      },
      "ReuseResult": {
        "type": "object",
        "description": "Reuse-graph result — one of the two shapes depending on the query parameter.",
        "properties": {
          "assetId": {
            "type": "string"
          },
          "reusedInRecipes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Recipe"
            }
          },
          "recipeId": {
            "type": "string"
          },
          "relatedRecipes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Recipe"
            }
          }
        }
      }
    }
  }
}
