{"openapi":"3.1.0","info":{"title":"Abil Shrestha portfolio API","version":"1.0.0","summary":"Read-only JSON API for the profile, projects and blog posts on www.abilshr.com.","description":"Public and unauthenticated. Errors use RFC 9457 problem details (application/problem+json) with `code` and `hint` members. The same content is available as markdown (send `Accept: text/markdown` to any page) and over MCP at /mcp.","contact":{"name":"Abil Shrestha","url":"https://www.abilshr.com/"}},"servers":[{"url":"https://www.abilshr.com"}],"externalDocs":{"description":"Developer guide","url":"https://www.abilshr.com/developers"},"security":[],"paths":{"/api/v1":{"get":{"operationId":"getApiIndex","summary":"List the API endpoints","responses":{"200":{"description":"Endpoint index","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/profile":{"get":{"operationId":"getProfile","summary":"Who Abil Shrestha is and where to find him","responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/projects":{"get":{"operationId":"listProjects","summary":"Projects with links, descriptions and years","responses":{"200":{"description":"Projects","content":{"application/json":{"schema":{"type":"object","required":["projects"],"properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/posts":{"get":{"operationId":"listPosts","summary":"Published blog posts, newest first","description":"Cursor-paginated. Pass next_cursor from a response back as cursor to get the next page; next_cursor is null on the last page.","parameters":[{"name":"limit","in":"query","required":false,"description":"Posts per page.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from a previous response's next_cursor. Omit to start from the newest post.","schema":{"type":"string"}}],"responses":{"200":{"description":"One page of posts","content":{"application/json":{"schema":{"type":"object","required":["posts","next_cursor"],"properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/PostSummary"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page, or null on the last page."}}}}}},"400":{"description":"limit or cursor is invalid","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/posts/{slug}":{"get":{"operationId":"getPost","summary":"One blog post with its full text as markdown","parameters":[{"name":"slug","in":"path","required":true,"description":"Post slug from listPosts, e.g. \"droid-maxxing\".","schema":{"type":"string","pattern":"^[\\w-]+$"}}],"responses":{"200":{"description":"Post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}},"404":{"description":"No published post has this slug","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/health":{"get":{"operationId":"getHealth","summary":"API status","responses":{"200":{"description":"API is up","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","const":"ok"}}}}}},"405":{"description":"The API is read-only; only GET is supported","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"The server failed to handle the request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other error, as problem details","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"schemas":{"ApiIndex":{"type":"object","required":["name","description","openapi","endpoints"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"openapi":{"type":"string","format":"uri"},"endpoints":{"type":"object","additionalProperties":{"type":"string"}}}},"Link":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string"},"url":{"type":"string","format":"uri"}}},"Profile":{"type":"object","required":["name","tagline","location","url","links"],"properties":{"name":{"type":"string"},"tagline":{"type":"string"},"location":{"type":"string"},"url":{"type":"string","format":"uri"},"links":{"type":"array","items":{"$ref":"#/components/schemas/Link"}}}},"Project":{"type":"object","required":["name","url","description","year"],"properties":{"name":{"type":"string"},"url":{"type":"string","format":"uri"},"description":{"type":"string"},"year":{"type":"string"}}},"PostSummary":{"type":"object","required":["slug","title","date","summary","url"],"properties":{"slug":{"type":"string"},"title":{"type":"string"},"date":{"type":"string","format":"date"},"summary":{"type":"string"},"url":{"type":"string","format":"uri"}}},"Post":{"allOf":[{"$ref":"#/components/schemas/PostSummary"},{"type":"object","required":["description","markdown"],"properties":{"description":{"type":"string"},"markdown":{"type":"string","description":"Full post as markdown."}}}]},"Problem":{"type":"object","description":"RFC 9457 problem details with `code` and `hint` extension members.","required":["type","title","status","code","detail","hint"],"properties":{"type":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","enum":["invalid_parameter","post_not_found","endpoint_not_found","method_not_allowed","internal_error"]},"detail":{"type":"string"},"hint":{"type":"string"},"docs":{"type":"string","format":"uri"}}}}}}