{
  "openapi": "3.1.0",
  "info": {
    "title": "Jon Rezin Public API",
    "version": "1.0.0",
    "description": "Public, agent-friendly API for Jon Rezin's credits, services, and inquiries. Agents welcome — identify via User-Agent: YourAgent/1.0 (on-behalf-of: user@example.com).",
    "contact": { "email": "info@9starmedia.com", "url": "https://jonrezin.com" },
    "license": { "name": "All rights reserved", "url": "https://jonrezin.com/terms.html" }
  },
  "servers": [{ "url": "https://jonrezin.com" }],
  "tags": [
    { "name": "Bio",     "description": "Structured bio, services, awards, availability." },
    { "name": "Catalog", "description": "Discography, artists, releases, sitemap." },
    { "name": "Search",  "description": "Search and page summaries." },
    { "name": "Forms",   "description": "Inquiry intake, testimonials, webhooks." },
    { "name": "MCP",     "description": "Model Context Protocol discovery + HTTP transport." },
    { "name": "Admin",   "description": "Authenticated admin endpoints (releases, covers)." }
  ],
  "paths": {
    "/api/v1/jon.json": { "get": { "tags": ["Bio"], "summary": "Structured bio, services, awards", "description": "Returns Jon's bio, services, awards, contact, and availability in a single JSON document. Cached for crawlers.", "responses": { "200": { "description": "OK" } } } },
    "/api/v1/discography": {
      "get": {
        "tags": ["Catalog"],
        "description": "List mixed/produced/mastered records. Filter by artist, role, paginate via limit/offset.",
        "summary": "List mixed/produced/mastered records",
        "parameters": [
          { "name": "artist", "in": "query", "schema": { "type": "string" } },
          { "name": "role", "in": "query", "schema": { "enum": ["mixer","producer","engineer","mastering"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/v1/services": { "get": { "tags": ["Bio"], "summary": "Services offered", "responses": { "200": { "description": "OK" } } } },
    "/api/v1/availability": { "get": { "tags": ["Bio"], "summary": "Current booking window", "responses": { "200": { "description": "OK" } } } },
    "/api/v1/artists": { "get": { "tags": ["Catalog"], "summary": "List credited artists with genre tags", "responses": { "200": { "description": "OK" } } } },
    "/api/v1/sitemap.json": {
      "get": {
        "tags": ["Catalog"],
        "summary": "JSON sitemap for AI crawlers",
        "description": "Structured JSON version of the site sitemap. Includes URL, language, lastmod, and type (page, release, artist, post, video) for every public URL. Cached for 1 hour.",
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/v1/search": {
      "post": {
        "tags": ["Search"],
        "summary": "Semantic search across credits/services",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["q"], "properties": { "q": { "type": "string" } } } } } },
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/v1/inquire": {
      "post": {
        "tags": ["Forms"],
        "summary": "Submit an inquiry (agents + humans). Rate limited.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["purpose","contact"], "properties": { "purpose": { "type": "string" }, "contact": { "type": "string", "format": "email" }, "name": { "type": "string" }, "message": { "type": "string" }, "budget": { "type": "string" } } } } } },
        "responses": { "202": { "description": "Accepted" }, "429": { "description": "Rate limited" } }
      }
    },
    "/api/v1/webhooks": {
      "post": {
        "tags": ["Forms"],
        "summary": "Register a webhook for new release / availability events",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url","events","contact"], "properties": { "url": { "type": "string", "format": "uri" }, "events": { "type": "array", "items": { "enum": ["release.new","availability.changed"] } }, "contact": { "type": "string", "format": "email" } } } } } },
        "responses": { "201": { "description": "Registered" } }
      }
    },
    "/status.json": { "get": { "tags": ["Bio"], "summary": "Uptime + availability snapshot", "responses": { "200": { "description": "OK" } } } },
    "/api/v1/page.json": {
      "get": {
        "tags": ["Search"],
        "summary": "Agent-friendly structured summary of any public page",
        "description": "Returns a Schema.org WebPage descriptor for any known URL. When ?lang=<code> is set, the page title/description are returned in that language (supported: en, es, pt, ko, ja, zh, fr, de, sw, yo, id, vi, th, ar). Falls back to English for unknown codes or untranslated keys.",
        "parameters": [
          { "name": "url",  "in": "query", "schema": { "type": "string" }, "example": "/about" },
          { "name": "lang", "in": "query", "schema": { "type": "string", "enum": ["en","es","pt","ko","ja","zh","fr","de","sw","yo","id","vi","th","ar"], "default": "en" }, "example": "de" }
        ],
        "responses": { "200": { "description": "OK" }, "404": { "description": "Unknown page" } }
      }
    },
    "/api/v1/testimonials": {
      "get": { "tags": ["Forms"], "summary": "List approved public testimonials", "responses": { "200": { "description": "OK" } } }
    },
    "/api/v1/mcp": {
      "get": {
        "tags": ["MCP"],
        "summary": "MCP HTTP transport discovery",
        "description": "Returns a small discovery document listing the JSON-RPC 2.0 methods and tools available on the MCP HTTP transport. POST JSON-RPC requests to this same URL to invoke them.",
        "responses": { "200": { "description": "OK" } }
      },
      "post": {
        "tags": ["MCP"],
        "summary": "MCP JSON-RPC 2.0 endpoint",
        "description": "Implements Model Context Protocol over HTTP using JSON-RPC 2.0. Supports methods: initialize, tools/list, tools/call. Tools: getCredits(artist), searchReleases(query). Read-only catalog data; no auth required.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "required": ["jsonrpc","method"],
            "properties": {
              "jsonrpc": { "type": "string", "enum": ["2.0"] },
              "id":      { "oneOf": [{ "type": "string" }, { "type": "integer" }, { "type": "null" }] },
              "method":  { "type": "string", "enum": ["initialize","tools/list","tools/call","ping"] },
              "params":  { "type": "object" }
            }
          }, "examples": {
            "list": { "value": { "jsonrpc": "2.0", "id": 1, "method": "tools/list" } },
            "getCredits": { "value": { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "getCredits", "arguments": { "artist": "Bad Bunny" } } } },
            "searchReleases": { "value": { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "searchReleases", "arguments": { "query": "halftime" } } } }
          }}}
        },
        "responses": { "200": { "description": "JSON-RPC response (success or error)" } }
      }
    },
    "/api/v1/vitals": {
      "post": {
        "summary": "Submit Core Web Vitals metric (client beacon)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["name","value"], "properties": { "name": { "enum": ["LCP","INP","CLS","FCP","TTFB"] }, "value": { "type": "number" }, "rating": { "type": "string" }, "path": { "type": "string" } } } } } },
        "responses": { "204": { "description": "Accepted" } }
      }
    },
    "/api/og": {
      "get": {
        "summary": "OpenGraph image generator (1200x630 PNG)",
        "parameters": [
          { "name": "title", "in": "query", "schema": { "type": "string" } },
          { "name": "subtitle", "in": "query", "schema": { "type": "string" } },
          { "name": "kind", "in": "query", "schema": { "enum": ["default","artist","release","article"] } },
          { "name": "art", "in": "query", "schema": { "type": "string", "format": "uri" } }
        ],
        "responses": { "200": { "description": "PNG image" } }
      }
    },
    "/api/v1/admin/release": {
      "get": {
        "summary": "Read release(s). No slug param → list all; with slug → single record.",
        "tags": ["Admin"],
        "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
        "parameters": [{ "name": "slug", "in": "query", "schema": { "type": "string" } }],
        "responses": { "200": { "description": "OK" }, "401": { "description": "Unauthorized" }, "404": { "description": "Not found" } }
      },
      "post": {
        "summary": "Create a new release: append to js/discography-data.js and generate releases/<slug>.html",
        "tags": ["Admin"],
        "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object", "required": ["title","artist"],
            "properties": {
              "title": { "type": "string", "maxLength": 200 },
              "artist": { "type": "string", "maxLength": 200 },
              "slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,119}$" },
              "album": { "type": "string" },
              "cover": { "type": "string" },
              "released": { "type": "string" },
              "credits": { "type": "string", "default": "MIXING" },
              "success": { "type": "string" },
              "successShort": { "type": "string" },
              "label": { "type": "string" },
              "description": { "type": "string" },
              "spotify": { "type": "string", "format": "uri" },
              "apple":   { "type": "string", "format": "uri" },
              "youtube": { "type": "string", "format": "uri" },
              "tidal":   { "type": "string", "format": "uri" },
              "deezer":  { "type": "string", "format": "uri" },
              "soundcloud": { "type": "string", "format": "uri" }
            }
          }}}
        },
        "responses": { "201": { "description": "Created" }, "400": { "description": "Invalid" }, "401": { "description": "Unauthorized" }, "409": { "description": "slug_exists" } }
      },
      "patch": {
        "summary": "Patch post-release mutable fields: success | streaming | cover.",
        "tags": ["Admin"],
        "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
        "parameters": [{ "name": "slug", "in": "query", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": {
              "success": { "type": "string" },
              "cover": { "type": "string" },
              "streaming": {
                "type": "object",
                "properties": {
                  "spotify": { "type": "string", "format": "uri" },
                  "apple":   { "type": "string", "format": "uri" },
                  "youtube": { "type": "string", "format": "uri" },
                  "tidal":   { "type": "string", "format": "uri" },
                  "deezer":  { "type": "string", "format": "uri" },
                  "soundcloud": { "type": "string", "format": "uri" }
                }
              }
            }
          }}}
        },
        "responses": { "200": { "description": "Patched" }, "401": { "description": "Unauthorized" }, "404": { "description": "Not found" } }
      },
      "delete": {
        "summary": "Remove a release from grid and delete its HTML page.",
        "tags": ["Admin"],
        "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
        "parameters": [{ "name": "slug", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Deleted" }, "401": { "description": "Unauthorized" }, "404": { "description": "Not found" } }
      }
    },
    "/api/v1/admin/release-cover": {
      "post": {
        "summary": "Upload cover image. Stored at /images/albums/disc-<slug>.<ext>. Max 5 MB.",
        "tags": ["Admin"],
        "security": [{ "bearerAuth": [] }, { "sessionCookie": [] }],
        "requestBody": {
          "required": true,
          "content": { "multipart/form-data": { "schema": {
            "type": "object", "required": ["slug","file"],
            "properties": {
              "slug": { "type": "string" },
              "file": { "type": "string", "format": "binary" }
            }
          }}}
        },
        "responses": { "201": { "description": "Saved" } }
      }
    },
    "/api/v1/preview": {
      "get": {
        "summary": "Server-side preview lookup — iTunes → Deezer → Spotify fallback.",
        "parameters": [
          { "name": "artist", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "track", "in": "query", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "Found" }, "404": { "description": "No preview available" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Env var ADMIN_API_TOKEN; send 'Authorization: Bearer <token>'" },
      "sessionCookie": { "type": "apiKey", "in": "cookie", "name": "adm_sid" }
    }
  }
}
