{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "kmshack.kr Public Profile API",
    "summary": "Read-only public portfolio data for Minsoo Kim (kmshack).",
    "description": "Use this API when an agent or developer tool needs authoritative public profile, selected-project, or writing metadata from kmshack.kr without scraping presentation HTML. The API is unauthenticated and read-only; it cannot operate apps, access accounts, send messages, or perform transactions.",
    "version": "1.0.0",
    "license": {
      "name": "All rights reserved",
      "url": "https://kmshack.kr/developers/#license"
    }
  },
  "servers": [
    {
      "url": "https://kmshack.kr",
      "description": "Canonical production site"
    }
  ],
  "tags": [
    {
      "name": "Discovery",
      "description": "Find the API contract and its published resources."
    },
    {
      "name": "Profile",
      "description": "Retrieve the canonical public profile for Minsoo Kim (kmshack)."
    },
    {
      "name": "Projects",
      "description": "Retrieve selected indie and professional portfolio work."
    },
    {
      "name": "Writing",
      "description": "Retrieve the current index of published blog posts."
    }
  ],
  "security": [],
  "paths": {
    "/api/index.json": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Get the API resource catalog",
        "description": "Use this operation first when a client needs links to the API documentation, OpenAPI contract, and available data resources.",
        "operationId": "getApiCatalog",
        "security": [],
        "responses": {
          "200": {
            "description": "The API catalog and canonical resource URLs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiCatalog"
                }
              }
            }
          },
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "406": {"$ref": "#/components/responses/NotAcceptable"},
          "502": {"$ref": "#/components/responses/UpstreamUnavailable"}
        }
      }
    },
    "/api/profile.json": {
      "get": {
        "tags": ["Profile"],
        "summary": "Get Minsoo Kim's public profile",
        "description": "Use this operation to retrieve canonical public identity, focus areas, and verified external profile links for Minsoo Kim, also known as kmshack.",
        "operationId": "getPublicProfile",
        "security": [],
        "responses": {
          "200": {
            "description": "The current public profile and its schema/update metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileResponse"
                }
              }
            }
          },
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "406": {"$ref": "#/components/responses/NotAcceptable"},
          "502": {"$ref": "#/components/responses/UpstreamUnavailable"}
        }
      }
    },
    "/api/projects.json": {
      "get": {
        "tags": ["Projects"],
        "summary": "List selected portfolio projects",
        "description": "Use this operation to retrieve concise, typed summaries of the indie and professional products represented on kmshack.kr. Project names identify portfolio work and do not make this site an official publication of those products or companies.",
        "operationId": "listSelectedProjects",
        "security": [],
        "responses": {
          "200": {
            "description": "The selected project list and its schema/update metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectsResponse"
                }
              }
            }
          },
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "406": {"$ref": "#/components/responses/NotAcceptable"},
          "502": {"$ref": "#/components/responses/UpstreamUnavailable"}
        }
      }
    },
    "/api/posts.json": {
      "get": {
        "tags": ["Writing"],
        "summary": "List published blog posts",
        "description": "Use this operation to discover current kmshack.kr writing with canonical URLs, publication dates, descriptions, tags, and language metadata. Check each date before relying on older Android implementation details.",
        "operationId": "listPublishedPosts",
        "security": [],
        "responses": {
          "200": {
            "description": "The blog post index generated from the published Jekyll collection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostsResponse"
                }
              }
            }
          },
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "406": {"$ref": "#/components/responses/NotAcceptable"},
          "502": {"$ref": "#/components/responses/UpstreamUnavailable"}
        }
      }
    }
  },
  "components": {
    "responses": {
      "NotFound": {
        "description": "No API resource exists at the requested path.",
        "content": {
          "application/problem+json": {
            "schema": {"$ref": "#/components/schemas/Problem"}
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The API resource does not support the requested HTTP method.",
        "headers": {
          "Allow": {
            "description": "HTTP methods supported by the resource.",
            "schema": {"type": "string", "examples": ["GET, HEAD, OPTIONS"]}
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {"$ref": "#/components/schemas/Problem"}
          }
        }
      },
      "NotAcceptable": {
        "description": "The API cannot produce a representation accepted by the client.",
        "content": {
          "application/problem+json": {
            "schema": {"$ref": "#/components/schemas/Problem"}
          }
        }
      },
      "UpstreamUnavailable": {
        "description": "The static origin did not produce a usable API response.",
        "content": {
          "application/problem+json": {
            "schema": {"$ref": "#/components/schemas/Problem"}
          }
        }
      }
    },
    "schemas": {
      "ApiCatalog": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "version", "description", "documentation", "openapi", "endpoints"],
        "properties": {
          "name": {"type": "string", "description": "Public name of the API."},
          "version": {"type": "string", "description": "Semantic version of the API contract."},
          "description": {"type": "string", "description": "Concise scope and access description."},
          "documentation": {"type": "string", "format": "uri", "description": "Canonical human-readable developer portal."},
          "openapi": {"type": "string", "format": "uri", "description": "Canonical OpenAPI document URL."},
          "endpoints": {
            "type": "array",
            "description": "Canonical URLs of the public data resources.",
            "items": {"type": "string", "format": "uri"}
          }
        }
      },
      "ProfileResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["schemaVersion", "lastUpdated", "profile"],
        "properties": {
          "schemaVersion": {"type": "string", "description": "Version of this JSON response schema."},
          "lastUpdated": {"type": "string", "format": "date", "description": "Date the curated public profile data was last reviewed."},
          "profile": {"$ref": "#/components/schemas/Profile"}
        }
      },
      "Profile": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "alternateName", "jobTitle", "description", "url", "image", "yearsExperience", "focusAreas", "profiles"],
        "properties": {
          "name": {"type": "string", "description": "Public name used on the portfolio."},
          "alternateName": {"type": "string", "description": "Stable online identity used to disambiguate the person."},
          "jobTitle": {"type": "string", "description": "Professional role stated on the portfolio."},
          "description": {"type": "string", "description": "Short public profile summary."},
          "url": {"type": "string", "format": "uri", "description": "Canonical portfolio URL."},
          "image": {"type": "string", "format": "uri", "description": "Canonical public profile image URL."},
          "yearsExperience": {"type": "integer", "minimum": 0, "description": "Years of Android experience stated on the portfolio."},
          "focusAreas": {
            "type": "array",
            "description": "Engineering and product topics represented on the site.",
            "items": {"type": "string"}
          },
          "profiles": {
            "type": "array",
            "description": "Public external profiles used for identity cross-checking.",
            "items": {"$ref": "#/components/schemas/ExternalProfile"}
          }
        }
      },
      "ExternalProfile": {
        "type": "object",
        "additionalProperties": false,
        "required": ["service", "url"],
        "properties": {
          "service": {"type": "string", "description": "Name of the external profile service."},
          "url": {"type": "string", "format": "uri", "description": "Canonical public profile URL on that service."}
        }
      },
      "ProjectsResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["schemaVersion", "lastUpdated", "projects"],
        "properties": {
          "schemaVersion": {"type": "string", "description": "Version of this JSON response schema."},
          "lastUpdated": {"type": "string", "format": "date", "description": "Date the curated project data was last reviewed."},
          "projects": {
            "type": "array",
            "description": "Selected projects represented on the portfolio.",
            "items": {"$ref": "#/components/schemas/Project"}
          }
        }
      },
      "Project": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "category", "status", "description", "url", "metric", "technologies"],
        "properties": {
          "name": {"type": "string", "description": "Published project or product name."},
          "category": {"type": "string", "enum": ["indie", "professional"], "description": "Relationship of the work to the portfolio."},
          "status": {"type": "string", "enum": ["live", "portfolio"], "description": "Whether the item is linked as a live indie app or retained as professional portfolio work."},
          "description": {"type": "string", "description": "Concise description of the represented work."},
          "url": {"type": ["string", "null"], "format": "uri", "description": "Public product link when one is supplied, otherwise null."},
          "metric": {"type": ["string", "null"], "description": "Public metric stated on the portfolio, otherwise null."},
          "technologies": {
            "type": "array",
            "description": "Relevant technologies or engineering concerns.",
            "items": {"type": "string"}
          }
        }
      },
      "PostsResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["schemaVersion", "count", "posts"],
        "properties": {
          "schemaVersion": {"type": "string", "description": "Version of this JSON response schema."},
          "count": {"type": "integer", "minimum": 0, "description": "Number of posts in the response."},
          "posts": {
            "type": "array",
            "description": "Published posts in Jekyll's current ordering.",
            "items": {"$ref": "#/components/schemas/Post"}
          }
        }
      },
      "Post": {
        "type": "object",
        "additionalProperties": false,
        "required": ["title", "url", "date", "description", "tags", "language"],
        "properties": {
          "title": {"type": "string", "description": "Published article title."},
          "url": {"type": "string", "format": "uri", "description": "Canonical article URL."},
          "date": {"type": "string", "format": "date-time", "description": "Publication date with timezone."},
          "description": {"type": "string", "description": "Published description or a generated plain-text excerpt."},
          "tags": {
            "type": "array",
            "description": "Published article tags.",
            "items": {"type": "string"}
          },
          "language": {"type": "string", "description": "BCP 47-style language tag inherited from the article or site."}
        }
      },
      "Problem": {
        "type": "object",
        "additionalProperties": false,
        "required": ["type", "title", "status", "detail", "instance", "code", "message", "resolution", "documentation"],
        "properties": {
          "type": {"type": "string", "format": "uri", "description": "Stable problem-type URI that resolves to documentation."},
          "title": {"type": "string", "description": "Stable human-readable summary for the problem type."},
          "status": {"type": "integer", "minimum": 400, "maximum": 599, "description": "HTTP status code, equal to the response status."},
          "detail": {"type": "string", "description": "Request-specific explanation that does not expose internal details."},
          "instance": {"type": "string", "description": "URI reference identifying the request path that produced the error."},
          "code": {"type": "string", "pattern": "^[A-Z][A-Z0-9_]+$", "description": "Stable machine-readable error code."},
          "message": {"type": "string", "description": "Concise human-readable error message."},
          "resolution": {"type": "string", "description": "Concrete hint for correcting the request or choosing a valid resource."},
          "documentation": {"type": "string", "format": "uri", "description": "Canonical API error documentation URL."}
        }
      }
    }
  }
}
