{
  "openapi": "3.0.3",
  "info": {
    "title": "Executive Talents API Gateway — Control Plane",
    "version": "2.0.0",
    "description": "Application registration, scopes, cross-app grants, credentials, and signing keys (ADR-GW-1). See /developer-portal for every application registered here, and docs/gateway/CONTRACT.md in the repo for the full wire contract."
  },
  "servers": [{ "url": "/" }],
  "components": {
    "securitySchemes": {
      "adminAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "CONTROLPLANE_ADMIN_TOKEN — held by admin tooling (et-console-staging), not tied to any user session."
      },
      "internalAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "GATEWAY_INTERNAL_TOKEN — the same value the edge (cmd/gateway) sends; internal endpoints only."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string", "example": "CP1005" },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Application": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "slug": { "type": "string", "example": "hr-service" },
          "name": { "type": "string" },
          "description": { "type": "string", "nullable": true },
          "baseUrl": { "type": "string", "format": "uri" },
          "manifestUrl": { "type": "string", "format": "uri" },
          "documentationUrl": { "type": "string", "format": "uri", "nullable": true },
          "status": { "type": "string", "enum": ["active", "suspended", "revoked"] },
          "ownerEmail": { "type": "string", "nullable": true },
          "contact": { "type": "string", "nullable": true },
          "createdBy": { "type": "string", "nullable": true },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Scope": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "ownerApplicationId": { "type": "string", "format": "uuid" },
          "key": { "type": "string", "example": "employee.read" },
          "description": { "type": "string", "nullable": true },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "ScopeGrant": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "consumerApplicationId": { "type": "string", "format": "uuid" },
          "scopeId": { "type": "string", "format": "uuid" },
          "grantedBy": { "type": "string", "nullable": true },
          "grantedAt": { "type": "string", "format": "date-time" }
        }
      },
      "RegisterApplicationRequest": {
        "type": "object",
        "required": ["manifestUrl"],
        "properties": {
          "manifestUrl": { "type": "string", "format": "uri", "description": "The application's own GET .well-known/gateway-manifest URL." },
          "actor": { "type": "string", "description": "Free-text label for who/what is acting — this control plane has no user system of its own." }
        }
      },
      "GrantRequest": {
        "type": "object",
        "required": ["providerAppSlug", "scopeKey"],
        "properties": {
          "providerAppSlug": { "type": "string", "example": "hr-service" },
          "scopeKey": { "type": "string", "example": "employee.read" },
          "actor": { "type": "string" }
        }
      },
      "MintCredentialResponse": {
        "type": "object",
        "properties": {
          "clientId": { "type": "string" },
          "clientSecret": { "type": "string", "description": "Shown exactly once — never retrievable again; only its sha256 hash is stored." }
        }
      },
      "TokenResponse": {
        "type": "object",
        "properties": {
          "access_token": { "type": "string" },
          "token_type": { "type": "string", "example": "Bearer" },
          "expires_in": { "type": "integer", "example": 900 }
        }
      }
    }
  },
  "paths": {
    "/healthz": {
      "get": {
        "summary": "Health check",
        "tags": ["Operations"],
        "responses": { "200": { "description": "OK" }, "503": { "description": "DB unreachable" } }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "summary": "Public JWKS — active and retiring signing keys",
        "tags": ["Auth"],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "OAuth2 client-credentials token issuance",
        "tags": ["Auth"],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": ["grant_type", "client_id", "client_secret"],
                "properties": {
                  "grant_type": { "type": "string", "enum": ["client_credentials"] },
                  "client_id": { "type": "string" },
                  "client_secret": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Token issued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TokenResponse" } } } },
          "401": { "description": "Invalid client credentials", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/apps": {
      "get": {
        "summary": "Developer portal (JSON) — every active registered application",
        "tags": ["Developer Portal"],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Application" } } } }
          }
        }
      }
    },
    "/developer-portal": {
      "get": {
        "summary": "Developer portal (HTML) — browsable index of every active application and its docs",
        "tags": ["Developer Portal"],
        "responses": { "200": { "description": "OK", "content": { "text/html": {} } } }
      }
    },
    "/docs": {
      "get": {
        "summary": "Swagger UI for this control plane's own API (this page)",
        "tags": ["Developer Portal"],
        "responses": { "200": { "description": "OK", "content": { "text/html": {} } } }
      }
    },
    "/apps/{slug}/openapi.json": {
      "get": {
        "summary": "An application's own OpenAPI spec, fetched from its manifest's documentation URL and served through this gateway's domain",
        "tags": ["Developer Portal"],
        "parameters": [{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" }, "example": "hr-service" }],
        "responses": {
          "200": { "description": "OK — content-type passed through from the application's own response" },
          "404": { "description": "Unknown slug, or the application declared no documentation URL" },
          "502": { "description": "The application's documentation URL is unreachable or returned a non-200" }
        }
      }
    },
    "/apps/{slug}/docs": {
      "get": {
        "summary": "Swagger UI for one registered application, pointed at this gateway's own proxied copy of its spec",
        "tags": ["Developer Portal"],
        "parameters": [{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" }, "example": "hr-service" }],
        "responses": { "200": { "description": "OK", "content": { "text/html": {} } }, "404": { "description": "Unknown slug, or no documentation URL declared" } }
      }
    },
    "/applications": {
      "get": {
        "summary": "List all registered applications",
        "tags": ["Applications"],
        "security": [{ "adminAuth": [] }],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Application" } } } } }
        }
      },
      "post": {
        "summary": "Register an application from its manifest",
        "tags": ["Applications"],
        "security": [{ "adminAuth": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterApplicationRequest" } } } },
        "responses": {
          "201": { "description": "Registered", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Application" } } } },
          "409": { "description": "appId already registered", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "Manifest fetch/validation failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/applications/{id}": {
      "get": {
        "summary": "Get a registered application",
        "tags": ["Applications"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Application" } } } },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/applications/{id}/refresh": {
      "post": {
        "summary": "Re-fetch the application's manifest and upsert its scopes/routes",
        "tags": ["Applications"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Application" } } } },
          "404": { "description": "Not found" },
          "422": { "description": "Manifest fetch/validation failed, or appId changed" }
        }
      }
    },
    "/applications/{id}/scopes": {
      "get": {
        "summary": "List scopes owned by an application",
        "tags": ["Scopes"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Scope" } } } } } }
      }
    },
    "/applications/{id}/credentials": {
      "post": {
        "summary": "Mint a client_id/client_secret pair for an application",
        "tags": ["Credentials"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "actor": { "type": "string" } } } } } },
        "responses": {
          "201": { "description": "Minted — clientSecret shown once", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MintCredentialResponse" } } } },
          "422": { "description": "Application not active" }
        }
      }
    },
    "/credentials/{id}/revoke": {
      "post": {
        "summary": "Revoke a client credential",
        "tags": ["Credentials"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "reason": { "type": "string" } } } } } },
        "responses": { "204": { "description": "Revoked" }, "404": { "description": "Not found or already revoked" } }
      }
    },
    "/applications/{id}/grants": {
      "get": {
        "summary": "List an application's scope grants (as consumer)",
        "tags": ["Grants"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ScopeGrant" } } } } } }
      },
      "post": {
        "summary": "Grant this application (as consumer) a scope of another (provider)",
        "tags": ["Grants"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The consumer application's id." }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GrantRequest" } } } },
        "responses": {
          "201": { "description": "Granted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScopeGrant" } } } },
          "409": { "description": "Grant already exists" },
          "422": { "description": "Self-grant, or provider/scope not found" }
        }
      }
    },
    "/grants/{id}": {
      "delete": {
        "summary": "Revoke a scope grant",
        "tags": ["Grants"],
        "security": [{ "adminAuth": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "204": { "description": "Revoked" }, "404": { "description": "Not found" } }
      }
    },
    "/api/v1/gateway/internal/config": {
      "get": {
        "summary": "Config snapshot the edge polls (routes, JWKS, rate limits)",
        "tags": ["Internal (edge only)"],
        "security": [{ "internalAuth": [] }],
        "responses": { "200": { "description": "OK" }, "304": { "description": "Not modified (If-None-Match)" } }
      }
    },
    "/api/v1/gateway/internal/usage": {
      "post": {
        "summary": "Usage event batch ingest from the edge",
        "tags": ["Internal (edge only)"],
        "security": [{ "internalAuth": [] }],
        "responses": { "202": { "description": "Accepted" } }
      }
    }
  }
}
