{
  "openapi": "3.1.0",
  "info": {
    "title": "xisra.com API",
    "summary": "Public catalog of Israel Kouperman's site, tools, posts, and projects.",
    "description": "Read-only catalog for agents and scripts. Authenticated proposal CRUD is documented but requires a Clerk API key.\n\nMarkdown: send `Accept: text/markdown` on HTML pages.\nErrors: JSON objects with `error`, `message`, and `hint`.\nThis document: http://localhost:42467/openapi.json.",
    "version": "1.0.0",
    "contact": {
      "name": "Israel Kouperman",
      "email": "me@xisra.com",
      "url": "http://localhost:42467"
    }
  },
  "servers": [
    {
      "url": "http://localhost:42467",
      "description": "This origin"
    },
    {
      "url": "https://xisra.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Catalog",
      "description": "Unauthenticated site index for agents."
    },
    {
      "name": "Proposals",
      "description": "Authenticated freelance proposal store. Clerk bearer token required."
    }
  ],
  "paths": {
    "/api/site": {
      "get": {
        "operationId": "getSiteCatalog",
        "tags": [
          "Catalog"
        ],
        "summary": "Site catalog",
        "description": "Identity, contact, docs URLs, and counts of tools, posts, and projects.",
        "responses": {
          "200": {
            "description": "Catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteCatalog"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/site/tools": {
      "get": {
        "operationId": "listSiteTools",
        "tags": [
          "Catalog"
        ],
        "summary": "List developer tools",
        "description": "Browser tools published on /tools, with canonical paths.",
        "responses": {
          "200": {
            "description": "Tool list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolList"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/site/posts": {
      "get": {
        "operationId": "listSitePosts",
        "tags": [
          "Catalog"
        ],
        "summary": "List published posts",
        "description": "Blog posts with slug, title, description, and absolute URL.",
        "responses": {
          "200": {
            "description": "Post list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostList"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/site/projects": {
      "get": {
        "operationId": "listSiteProjects",
        "tags": [
          "Catalog"
        ],
        "summary": "List published projects",
        "description": "Portfolio projects with slug, title, and absolute URL.",
        "responses": {
          "200": {
            "description": "Project list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/proposals": {
      "get": {
        "operationId": "listProposals",
        "tags": [
          "Proposals"
        ],
        "summary": "List proposals",
        "description": "Requires a Clerk API key, M2M token, or admin session.",
        "security": [
          {
            "clerkBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalList"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin access required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Clerk or R2 is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/proposals/{slug}": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "Proposal slug, for example xplace_215194_hasadran-mvp.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getProposal",
        "tags": [
          "Proposals"
        ],
        "summary": "Get one proposal",
        "description": "Fetch a proposal by slug. Auth required.",
        "security": [
          {
            "clerkBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Clerk or R2 is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "putProposal",
        "tags": [
          "Proposals"
        ],
        "summary": "Create or replace a proposal",
        "description": "Upsert markdown plus front matter. Auth required.",
        "security": [
          {
            "clerkBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProposalUpsert"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved proposal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Clerk or R2 is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteProposal",
        "tags": [
          "Proposals"
        ],
        "summary": "Delete a proposal",
        "description": "Remove a proposal by slug. Auth required.",
        "security": [
          {
            "clerkBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteOk"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Clerk or R2 is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clerkBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Clerk user API key or M2M token. Admin session cookies also work from the back office."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error",
          "code",
          "message",
          "hint",
          "docs"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine-readable error code."
          },
          "code": {
            "type": "string",
            "description": "Same value as error, for clients that look for code."
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string",
            "description": "What to do next to resolve the error."
          },
          "docs": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "SiteCatalog": {
        "type": "object",
        "required": [
          "name",
          "docs",
          "pages"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "owner": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "contact": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "llmsTxt": {
            "type": "string",
            "format": "uri"
          },
          "sitemap": {
            "type": "string",
            "format": "uri"
          },
          "pages": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "services": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          }
        }
      },
      "ToolList": {
        "type": "object",
        "required": [
          "tools"
        ],
        "properties": {
          "tools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tool"
            }
          }
        }
      },
      "Tool": {
        "type": "object",
        "required": [
          "id",
          "title",
          "path",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PostList": {
        "type": "object",
        "required": [
          "posts"
        ],
        "properties": {
          "posts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Post"
            }
          }
        }
      },
      "Post": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "path",
          "url",
          "createdAt"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectList": {
        "type": "object",
        "required": [
          "projects"
        ],
        "properties": {
          "projects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            }
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "path",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ProposalList": {
        "type": "object",
        "required": [
          "proposals"
        ],
        "properties": {
          "proposals": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "ProposalEnvelope": {
        "type": "object",
        "required": [
          "proposal"
        ],
        "properties": {
          "proposal": {
            "type": "object"
          }
        }
      },
      "ProposalUpsert": {
        "type": "object",
        "required": [
          "title",
          "content"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectId": {
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "projectUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "client": {
            "type": [
              "string",
              "null"
            ]
          },
          "lang": {
            "type": "string",
            "enum": [
              "he",
              "en"
            ]
          },
          "password": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PUBLISHED"
            ]
          },
          "content": {
            "type": "string"
          },
          "pipeline": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "createdAt": {
            "type": "string"
          }
        }
      },
      "DeleteOk": {
        "type": "object",
        "required": [
          "ok",
          "slug"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "slug": {
            "type": "string"
          }
        }
      }
    }
  }
}