{
  "openapi": "3.1.0",
  "info": {
    "title": "Ganidhu Public API",
    "version": "1.0.0",
    "description": "Read-only public portfolio API for Ganidhu.com. It exposes only information intentionally published on the website."
  },
  "servers": [
    {
      "url": "https://ganidhu.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Ganidhu Developer Portal",
    "url": "https://ganidhu.com/developers"
  },
  "paths": {
    "/api/v1/profile": {
      "get": {
        "operationId": "getPublicProfile",
        "summary": "Get Ganidhu's public profile",
        "description": "Returns public identity, description, location, contact, social links, and currently visible creating items from Ganidhu.com.",
        "tags": [
          "Portfolio"
        ],
        "responses": {
          "200": {
            "description": "Public profile returned successfully.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileResponse"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/resources": {
      "get": {
        "operationId": "listPublicResources",
        "summary": "List Ganidhu's public resources",
        "description": "Returns resources currently marked visible on Ganidhu.com plus canonical links to the public gear and setup pages.",
        "tags": [
          "Portfolio"
        ],
        "responses": {
          "200": {
            "description": "Public resources returned successfully.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourcesResponse"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "RateLimitLimit": {
        "description": "Maximum request budget advertised for the current 60 second window.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "example": 120
        }
      },
      "RateLimitRemaining": {
        "description": "Approximate remaining request budget advertised to the client.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 119
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the advertised request window resets.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 60
        }
      },
      "RateLimitPolicy": {
        "description": "Quota policy using the IETF HTTPAPI structured RateLimit-Policy field.",
        "schema": {
          "type": "string",
          "example": "\"public-api\";q=120;w=60"
        }
      },
      "RetryAfter": {
        "description": "Seconds the client should wait before retrying after a 429 response.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 60
        }
      },
      "RateLimit": {
        "description": "Current service limit using the IETF HTTPAPI structured RateLimit field.",
        "schema": {
          "type": "string",
          "example": "\"public-api\";r=119;t=60"
        }
      }
    },
    "responses": {
      "MethodNotAllowed": {
        "description": "The endpoint is read-only and the HTTP method is not supported.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The client exceeded the request budget and should retry later.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "SocialLink": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "label",
          "href"
        ],
        "properties": {
          "label": {
            "type": "string",
            "description": "Public platform label."
          },
          "href": {
            "type": "string",
            "format": "uri",
            "description": "Canonical public profile URL."
          }
        }
      },
      "CreatingItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "href"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "href": {
            "type": "string"
          }
        }
      },
      "ProfileResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "descriptor",
          "statement",
          "location",
          "website",
          "contact",
          "socials",
          "creating"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Public name shown on Ganidhu.com."
          },
          "descriptor": {
            "type": "string",
            "description": "Short public role description."
          },
          "statement": {
            "type": "string",
            "description": "Public homepage introduction."
          },
          "location": {
            "type": "string",
            "description": "Public location at country level."
          },
          "website": {
            "type": "string",
            "format": "uri"
          },
          "contact": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "email"
            ],
            "properties": {
              "email": {
                "type": "string",
                "format": "email"
              }
            }
          },
          "socials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SocialLink"
            }
          },
          "creating": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreatingItem"
            }
          }
        }
      },
      "ResourceItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "description",
          "href"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "href": {
            "type": "string"
          }
        }
      },
      "ResourcesResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "resources",
          "related"
        ],
        "properties": {
          "resources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResourceItem"
            }
          },
          "related": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "gear",
              "setup"
            ],
            "properties": {
              "gear": {
                "type": "string",
                "format": "uri"
              },
              "setup": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message",
              "resolution"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code."
              },
              "message": {
                "type": "string",
                "description": "Human-readable error explanation."
              },
              "resolution": {
                "type": "string",
                "description": "Actionable next step for the caller."
              }
            }
          }
        }
      }
    }
  }
}
