{"info":{"title":"Portfolio API","version":"1.0.0","description":"Portfolio oRPC + REST API\n## Versioning and deprecation\n\nThe version is in the URL path (`/api/v1/...`). A breaking change ships as a new path segment (`/api/v2`); anything additive lands in place.\n\nNothing is removed silently. When an operation is scheduled for removal its responses start carrying a `Deprecation` header (draft-ietf-httpapi-deprecation-header) on the day the decision takes effect, and an RFC 8594 `Sunset` header once a removal date is committed — always at least 90 days out. Both are declared in `components.headers`, so an agent can watch for them mechanically rather than trusting this paragraph.\n\nEvery 4xx and 5xx body is the `Error` schema: a machine-readable `code`, a human-readable `message`, and a `data` object repeating the code and HTTP status. Input-validation failures additionally carry raw Zod `issues`."},"security":[{"cookieAuth":[]}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Better Auth session cookie"}},"schemas":{"Error":{"type":"object","title":"Error","description":"Every 4xx and 5xx response body. `code` is the machine-readable discriminator; `message` is human-readable and safe to surface. The HTTP status is on the response itself and repeated at `data.httpStatus`.","required":["code","message","data"],"properties":{"code":{"type":"string","description":"Machine-readable error code, e.g. NOT_FOUND. Matches `data.code`.","examples":["NOT_FOUND","BAD_REQUEST","UNAUTHORIZED","INTERNAL_SERVER_ERROR"]},"message":{"type":"string","description":"Human-readable explanation."},"data":{"type":"object","description":"Structured detail. Always carries `code` and `httpStatus`; typed errors declared by an operation spread their own fields in alongside.","required":["code","httpStatus"],"properties":{"code":{"type":"string","description":"Repeats the top-level `code`."},"httpStatus":{"type":"integer","description":"Repeats the response's HTTP status."},"errorCode":{"type":"string","description":"The originating domain error code, when the failure came from a domain service."},"details":{"type":["object","null"],"description":"Domain-supplied detail, when present."},"path":{"type":"string","description":"Dotted procedure path that failed."}},"additionalProperties":true},"issues":{"type":"array","description":"Raw Zod issues. Present only on input-validation failures.","items":{"type":"object","additionalProperties":true}}}}},"headers":{"Deprecation":{"schema":{"type":"string"},"description":"Present once an operation is deprecated (draft-ietf-httpapi-deprecation-header): the date the deprecation took effect. Absent today."},"Sunset":{"schema":{"type":"string","format":"date-time"},"description":"Present once a removal date is committed (RFC 8594), always at least 90 days after Deprecation first appears. Absent today."}}},"openapi":"3.1.1","paths":{"/api/v1/activity":{"get":{"operationId":"v1.activity.getCombined","summary":"Get combined portfolio activity","description":"Returns the GitHub contribution calendar, recent public GitHub events, and LeetCode solve stats in one payload, plus the GitHub username. Prefer the narrower `/activity/*` operations when only one source is needed.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"github":{"type":"object","properties":{"contributions":{"type":"object","properties":{"totalContributions":{"type":"integer","minimum":0,"maximum":9007199254740991},"weeks":{"type":"array","items":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"level":{"type":"integer","minimum":0,"maximum":4}},"required":["date","count","level"]}}},"required":["days"]}}},"required":["totalContributions","weeks"]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"repo":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"]},"payload":{"type":"object","properties":{"action":{"type":"string"},"ref":{"type":"string"},"commits":{"type":"array","items":{"type":"object","properties":{"sha":{"type":"string"},"message":{"type":"string"}},"required":["sha","message"]}}}},"createdAt":{"type":"string","format":"date-time"}},"required":["id","type","repo","payload","createdAt"]}},"username":{"type":"string"}},"required":["contributions","events","username"]},"leetcode":{"anyOf":[{"type":"object","properties":{"totalSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"totalQuestions":{"type":"integer","minimum":0,"maximum":9007199254740991},"easySolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"easyTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"mediumSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"mediumTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"hardSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"hardTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"ranking":{"type":"integer","minimum":0,"maximum":9007199254740991},"acceptanceRate":{"type":"number","minimum":0,"maximum":100},"contributionPoints":{"type":"integer","minimum":0,"maximum":9007199254740991},"streak":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["totalSolved","totalQuestions","easySolved","easyTotal","mediumSolved","mediumTotal","hardSolved","hardTotal","ranking","acceptanceRate","contributionPoints","streak"]},{"type":"null"}]}},"required":["github","leetcode"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity/github/contributions":{"get":{"operationId":"v1.activity.getGithubContributions","summary":"Get GitHub contributions","description":"Returns the GitHub contribution calendar as a total count plus per-week daily counts, the shape needed to render a contribution heatmap.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"totalContributions":{"type":"integer","minimum":0,"maximum":9007199254740991},"weeks":{"type":"array","items":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"level":{"type":"integer","minimum":0,"maximum":4}},"required":["date","count","level"]}}},"required":["days"]}}},"required":["totalContributions","weeks"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity/github/events":{"get":{"operationId":"v1.activity.getGithubEvents","summary":"Get GitHub events","description":"Returns recent public GitHub events (pushes, pull requests, etc.) with the repo name and URL for each — a short recent-activity feed, not full commit history. For the single most recent commit use `/activity/latest-commit`.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"repo":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"]},"payload":{"type":"object","properties":{"action":{"type":"string"},"ref":{"type":"string"},"commits":{"type":"array","items":{"type":"object","properties":{"sha":{"type":"string"},"message":{"type":"string"}},"required":["sha","message"]}}}},"createdAt":{"type":"string","format":"date-time"}},"required":["id","type","repo","payload","createdAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity/leetcode":{"get":{"operationId":"v1.activity.getLeetcodeStats","summary":"Get LeetCode stats","description":"Returns LeetCode progress: solved counts per difficulty, ranking, acceptance rate, and streak. Returns null when stats are unavailable.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"totalSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"totalQuestions":{"type":"integer","minimum":0,"maximum":9007199254740991},"easySolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"easyTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"mediumSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"mediumTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"hardSolved":{"type":"integer","minimum":0,"maximum":9007199254740991},"hardTotal":{"type":"integer","minimum":0,"maximum":9007199254740991},"ranking":{"type":"integer","minimum":0,"maximum":9007199254740991},"acceptanceRate":{"type":"number","minimum":0,"maximum":100},"contributionPoints":{"type":"integer","minimum":0,"maximum":9007199254740991},"streak":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["totalSolved","totalQuestions","easySolved","easyTotal","mediumSolved","mediumTotal","hardSolved","hardTotal","ranking","acceptanceRate","contributionPoints","streak"]},{"type":"null"}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity/latest-commit":{"get":{"operationId":"v1.activity.getLatestCommit","summary":"Get latest public commit","description":"Returns the most recent public commit across all repos with its sha, message, timestamp, and repo links. Returns null when GitHub is rate-limited or there is no recent activity — a null result means no data is available, not a failure.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"sha":{"type":"string"},"fullSha":{"type":"string"},"message":{"type":"string"},"url":{"type":"string","format":"uri"},"committedAt":{"type":"string"},"repoName":{"type":"string"},"repoUrl":{"type":"string","format":"uri"}},"required":["sha","fullSha","message","url","committedAt","repoName","repoUrl"]},{"type":"null"}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity/weekly-summary":{"get":{"operationId":"v1.activity.getWeeklySummary","summary":"Get cached weekly commit summary","description":"Returns an AI-written markdown summary of the past week's commits, regenerated by a Sunday cron and served from cache with `ageMs` and `isStale` metadata. Returns null when the cache is empty or older than 14 days.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"markdown":{"type":"string"},"generatedAt":{"type":"string"},"commitCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"sources":{"type":"array","items":{"type":"object","properties":{"sha":{"type":"string"},"message":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["sha","message","url"]}},"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"ageMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"isStale":{"type":"boolean"}},"required":["markdown","generatedAt","commitCount","sources","windowStart","windowEnd","ageMs","isStale"]},{"type":"null"}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions":{"post":{"operationId":"v1.ai.chat.createSession","summary":"Create an AI chat session","description":"Creates a persistent chat session for the visitor messaging thread, optionally tagged with the visitor's name and email. Use the returned session id with the other `/ai/chat/sessions` operations; `stream` does not require one.","tags":["AI"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"visitorName":{"type":"string","minLength":1,"maxLength":100},"visitorEmail":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"visitorName":{"type":"string"},"visitorEmail":{"anyOf":[{"type":"string"},{"type":"null"}]},"isActive":{"type":"boolean"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sessionId":{"type":"string","format":"uuid"},"role":{"enum":["visitor","owner","system"],"type":"string"},"content":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","sessionId","role","content","isRead","createdAt"]}},"unreadCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","visitorName","visitorEmail","isActive","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions/{id}":{"get":{"operationId":"v1.ai.chat.getSession","summary":"Get an AI chat session","description":"Returns a session's metadata — visitor name and email, active state, and timestamps — by its UUID. Use `getMessages` for the conversation itself.","tags":["AI"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"visitorName":{"type":"string"},"visitorEmail":{"anyOf":[{"type":"string"},{"type":"null"}]},"isActive":{"type":"boolean"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sessionId":{"type":"string","format":"uuid"},"role":{"enum":["visitor","owner","system"],"type":"string"},"content":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","sessionId","role","content","isRead","createdAt"]}},"unreadCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","visitorName","visitorEmail","isActive","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions/{sessionId}/messages":{"get":{"operationId":"v1.ai.chat.getMessages","summary":"List AI chat messages for a session","description":"Returns a session's messages, oldest-first. Prefer the opaque `cursor` from a previous response's `nextCursor` — chat history is append-heavy and read backwards, so `offset` re-shows or skips messages when new ones arrive mid-read. `offset` remains supported for existing callers. Pass `markAsRead: true` to also clear the visitor's unread count in the same call.","tags":["AI"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":-9007199254740991,"maximum":100,"exclusiveMinimum":0,"default":50},"allowEmptyValue":true,"allowReserved":true},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true},{"name":"cursor","in":"query","schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"markAsRead","in":"query","schema":{"type":"boolean"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sessionId":{"type":"string","format":"uuid"},"role":{"enum":["visitor","owner","system"],"type":"string"},"content":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","sessionId","role","content","isRead","createdAt"]}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null}},"required":["messages","hasMore"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"v1.ai.chat.sendMessage","summary":"Send a message to an AI chat session","description":"Appends a plain-text message (max 2000 chars, visitor role by default) to the session. This only persists the message — it does not generate an AI reply; use `stream` for that.","tags":["AI"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":2000},"role":{"enum":["visitor","owner","system"],"type":"string","default":"visitor"}},"required":["content"]}},"required":["data"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sessionId":{"type":"string","format":"uuid"},"role":{"enum":["visitor","owner","system"],"type":"string"},"content":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","sessionId","role","content","isRead","createdAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions/{sessionId}/messages/read":{"post":{"operationId":"v1.ai.chat.markAsRead","summary":"Mark AI chat messages as read","description":"Marks the session's unread messages as read for the given role (visitor by default), clearing the count returned by `getUnreadCount`.","tags":["AI"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"role":{"enum":["visitor","owner"],"type":"string"}},"required":["role"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"const":true}},"required":["success"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions/{sessionId}/messages/unread-count":{"get":{"operationId":"v1.ai.chat.getUnreadCount","summary":"Get unread AI chat message count","description":"Returns how many messages in the session the visitor has not read yet. Poll this to badge the chat widget without fetching full messages.","tags":["AI"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["count"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/sessions/{sessionId}/close":{"post":{"operationId":"v1.ai.chat.closeSession","summary":"Close an AI chat session","description":"Marks the session inactive and returns the updated session record. Call when the visitor ends the conversation; create a new session to chat again.","tags":["AI"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"visitorName":{"type":"string"},"visitorEmail":{"anyOf":[{"type":"string"},{"type":"null"}]},"isActive":{"type":"boolean"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sessionId":{"type":"string","format":"uuid"},"role":{"enum":["visitor","owner","system"],"type":"string"},"content":{"type":"string"},"isRead":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","sessionId","role","content","isRead","createdAt"]}},"unreadCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","visitorName","visitorEmail","isActive","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/chat/stream":{"post":{"operationId":"v1.ai.chat.stream","summary":"Stream an AI chat response","description":"Streams an AI-generated reply as an event stream for the given UI-message history, with optional model override, current-page context, and web search. Rate limited per client; unlike `sendMessage`, this is what actually invokes the AI.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","minItems":1,"items":{}},"threadId":{"type":"string"},"id":{"type":"string"},"clientId":{"type":"string"},"model":{"type":"string"},"currentPage":{"type":"string"},"webSearch":{"type":"boolean"}},"required":["messages"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/demo/homepage":{"post":{"operationId":"v1.ai.demo.homepage","summary":"Homepage live-MCP demo (public, no auth)","description":"Streams a portfolio-agent response for the homepage live-MCP demo widget, creating a throwaway session id per call. Rate limited per client; use `stream` for the full chat experience.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","minItems":1,"items":{}},"model":{"type":"string"}},"required":["messages"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/demo/mcp-trace":{"post":{"operationId":"v1.ai.demo.mcpTrace","summary":"/mcp/try multi-tool trace demo (public, no auth)","description":"Streams a response from the guide agent behind the /mcp/try page, which demonstrates multi-tool MCP call traces. Same throwaway-session, rate-limited behavior as the homepage demo.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","minItems":1,"items":{}},"model":{"type":"string"}},"required":["messages"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/tools":{"get":{"operationId":"v1.ai.tools.list","summary":"List AI chat tools (public registry)","description":"Returns the catalog of tools the portfolio AI agent can call — id, category, JSON-schema input definition, example input and output, and a suggested prompt per tool. Static metadata only; nothing is executed.","tags":["AI"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"category":{"enum":["read","action"],"type":"string"},"description":{"type":"string"},"inputSchema":{"type":"object","properties":{"type":{"const":"object"},"properties":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"},"format":{"type":"string"},"enum":{"type":"array","items":{"type":"string"}}},"required":["type"]}},"required":{"type":"array","items":{"type":"string"}}},"required":["type","properties","required"]},"exampleInput":{},"exampleOutput":{},"suggestedPrompt":{"type":"string"}},"required":["id","category","description","inputSchema","suggestedPrompt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ai/tools/send-email-to-bo":{"post":{"operationId":"v1.ai.tools.sendEmailToBo","summary":"Send an email to Bo from the AI chat tool","description":"Actually delivers an email to the site owner with the given subject, body, and visitor details — a real action, not a demo. Rate limited per session and IP; the body should be written as the visitor, not the agent.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"maxLength":140,"description":"The email subject line"},"body":{"type":"string","minLength":10,"maxLength":4000,"description":"The email body (plain text). Should be written as the visitor, not the agent."},"visitorName":{"type":"string","minLength":1,"maxLength":120,"description":"The visitor's display name so Bo knows who the email is from."},"replyTo":{"type":"string","format":"email","description":"Optional reply-to email address. If the visitor shares one, include it so Bo can respond."}},"required":["subject","body","visitorName"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"emailId":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts":{"post":{"operationId":"v1.contacts.create","summary":"Create contact submission","description":"Stores a contact-form submission (name, email, subject, message) and notifies the site owner. The optional `website` field is a spam honeypot and must be left empty — any value rejects the request as a bot.","tags":["Contacts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"email":{"type":"string","format":"email"},"subject":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"website":{"type":"string"}},"required":["name","email","subject","message"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"subject":{"type":"string"},"message":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","email","subject","message","createdAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events":{"get":{"operationId":"v1.events.list","summary":"List timeline events","description":"Returns all resume timeline entries — employment, education, certifications, projects, awards, and volunteer work — each with dates, company/location, and the skills exercised there.","tags":["Events"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"logoFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"skills":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","eventId","name","description","createdAt","updatedAt"]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","type","title","description","company","location","logoFileId","startDate","endDate","link","skills","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events/{id}":{"get":{"operationId":"v1.events.getById","summary":"Get event by id","description":"Returns a single timeline event with its skills, given an id from `list`. Responds 404 for unknown ids.","tags":["Events"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"logoFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"skills":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","eventId","name","description","createdAt","updatedAt"]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","type","title","description","company","location","logoFileId","startDate","endDate","link","skills","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/feature-flags":{"get":{"operationId":"v1.featureFlags.list","summary":"List feature flags","description":"Every flag in the manifest, each evaluated for the current caller. PostHog applies targeting and rollout server-side, so this reflects what THIS request sees rather than a global on/off.","tags":["Feature Flags"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"PostHog wire key, e.g. 'mvp'."},"enabled":{"type":"boolean","description":"Evaluated for the current caller, not a global on/off."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"From the typed manifest, not PostHog."}},"required":["key","enabled","description"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/feature-flags/{key}":{"get":{"operationId":"v1.featureFlags.evaluate","summary":"Evaluate a feature flag","description":"Resolve one flag for the current caller. An unknown key returns `enabled: false` with a reason rather than a 404 — a client asking about a retired flag should get a usable answer, not an error.","tags":["Feature Flags"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"key":{"type":"string"},"reason":{"type":"string","description":"Why the flag evaluated the way it did."}},"required":["enabled","key"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/featured-skills":{"get":{"operationId":"v1.featuredSkills.list","summary":"List featured skills","description":"Returns the curated homepage skill highlights — name, icon, link, and a `usedIn` blurb — with a `displayOrder` for rendering. Distinct from `/skills`, which lists the skills attached to resume timeline events.","tags":["Featured Skills"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"iconFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"iconFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"link":{"type":"string"},"usedIn":{"anyOf":[{"type":"string"},{"type":"null"}]},"displayOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","iconFileId","iconFile","link","usedIn","displayOrder","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/featured-skills/{id}":{"get":{"operationId":"v1.featuredSkills.getById","summary":"Get featured skill by id","description":"Returns a single featured-skill entry, including its resolved icon file, given an id from `list`.","tags":["Featured Skills"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"iconFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"iconFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"link":{"type":"string"},"usedIn":{"anyOf":[{"type":"string"},{"type":"null"}]},"displayOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","iconFileId","iconFile","link","usedIn","displayOrder","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/games":{"get":{"operationId":"v1.games.list","summary":"List games","description":"Returns the mini-games built for this site, each with a title, description, icon, and the link where it can be played.","tags":["Games"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string"},"link":{"type":"string"},"iconFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"iconFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","description","link","iconFileId","iconFile","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/games/{id}":{"get":{"operationId":"v1.games.getById","summary":"Get game by id","description":"Returns a single game entry, including its resolved icon file, given an id from `list`. Responds 404 for unknown ids.","tags":["Games"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string"},"link":{"type":"string"},"iconFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"iconFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","description","link","iconFileId","iconFile","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/health":{"get":{"operationId":"v1.health.check","summary":"Health check","description":"Shallow by default — DB ping only. Use `?liveness=true` to skip dependencies or `?deep=true` to also ping Redis.","tags":["Health"],"parameters":[{"name":"deep","in":"query","schema":{"anyOf":[{"type":"boolean"},{"type":"string"}]},"allowEmptyValue":true,"allowReserved":true},{"name":"liveness","in":"query","schema":{"anyOf":[{"type":"boolean"},{"type":"string"}]},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"enum":["healthy","unhealthy","degraded"],"type":"string"},"database":{"enum":["connected","disconnected","skipped"],"type":"string"},"redis":{"enum":["connected","disconnected"],"type":"string"}},"required":["status","database"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/posts":{"get":{"operationId":"v1.posts.list","summary":"List published posts","description":"Returns metadata for every published writing post — slug, title, excerpt, tags, reading time, and optional related project slug — without the body. The post body is MDX rendered on the site, not served here.","tags":["Writing"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"featured":{"type":"boolean"},"relatedProjectSlug":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroImageId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"readingTimeMinutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"tags":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"]}}},"required":["id","slug","title","excerpt","visibility","publishedAt","updatedAt","createdAt","featured","relatedProjectSlug","heroImageId","readingTimeMinutes","tags"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/posts/{slug}":{"get":{"operationId":"v1.posts.getBySlug","summary":"Get a post by slug","description":"Returns a single published post's metadata by its URL slug. Unpublished or unknown slugs respond 404.","tags":["Writing"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"featured":{"type":"boolean"},"relatedProjectSlug":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroImageId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"readingTimeMinutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"tags":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"]}}},"required":["id","slug","title","excerpt","visibility","publishedAt","updatedAt","createdAt","featured","relatedProjectSlug","heroImageId","readingTimeMinutes","tags"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/profiles":{"get":{"operationId":"v1.profile.list","summary":"List profiles","description":"Returns the site owner's profile — name, bio, summary, contact details, languages, and images — as a single-element array; this is a single-profile site.","tags":["Profile"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"bio":{"type":"string"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"contactImageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"contactImageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"contactText":{"anyOf":[{"type":"string"},{"type":"null"}]},"languages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"proficiency":{"enum":["native","fluent","conversational","basic"],"type":"string"}},"required":["name","proficiency"]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","bio","summary","email","phone","location","imageFileId","imageFile","contactImageFileId","contactImageFile","contactText","languages","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/profiles/{id}":{"get":{"operationId":"v1.profile.getById","summary":"Get profile by id","description":"Returns the profile only when `id` matches the single profile's id; any other id responds 404. Prefer `list`, which needs no id.","tags":["Profile"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"bio":{"type":"string"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"contactImageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"contactImageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"contactText":{"anyOf":[{"type":"string"},{"type":"null"}]},"languages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"proficiency":{"enum":["native","fluent","conversational","basic"],"type":"string"}},"required":["name","proficiency"]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","bio","summary","email","phone","location","imageFileId","imageFile","contactImageFileId","contactImageFile","contactText","languages","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects":{"get":{"operationId":"v1.projects.list","summary":"List projects","description":"Returns every published portfolio project with its slug, brief description, lifecycle (building/shipping/retired), tech stack, hero image, and external link, newest first. Use `getBySlug` for one project, or `listFeatured`/`listArchive` for curated subsets.","tags":["Projects"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"lifecycle":{"enum":["building","shipping","retired"],"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"briefDescription":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroMetric":{"anyOf":[{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"required":["value","label"]},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"technologies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","slug","lifecycle","visibility","briefDescription","description","heroMetric","imageFileId","imageFile","technologies","link","featured","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects/featured":{"get":{"operationId":"v1.projects.listFeatured","summary":"List featured projects","description":"Returns only published projects flagged as featured — the ones highlighted on the homepage. A subset of `list`.","tags":["Projects"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"lifecycle":{"enum":["building","shipping","retired"],"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"briefDescription":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroMetric":{"anyOf":[{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"required":["value","label"]},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"technologies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","slug","lifecycle","visibility","briefDescription","description","heroMetric","imageFileId","imageFile","technologies","link","featured","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects/archive":{"get":{"operationId":"v1.projects.listArchive","summary":"List archive projects","description":"Returns the archive view: published non-featured projects plus archived ones, newest first. The complement of `listFeatured`.","tags":["Projects"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"lifecycle":{"enum":["building","shipping","retired"],"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"briefDescription":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroMetric":{"anyOf":[{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"required":["value","label"]},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"technologies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","slug","lifecycle","visibility","briefDescription","description","heroMetric","imageFileId","imageFile","technologies","link","featured","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects/{slug}":{"get":{"operationId":"v1.projects.getBySlug","summary":"Get project by slug","description":"Returns a single project by its URL slug, including the full description, technologies, and hero image. Responds 404 when no project has that slug.","tags":["Projects"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"lifecycle":{"enum":["building","shipping","retired"],"type":"string"},"visibility":{"enum":["draft","published","archived","hidden"],"type":"string"},"briefDescription":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"heroMetric":{"anyOf":[{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"required":["value","label"]},{"type":"null"}]},"imageFileId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"imageFile":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bucket":{"type":"string"},"storageKey":{"type":"string"},"storageUrl":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"filename":{"type":"string"},"originalFilename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0},{"type":"null"}]},"category":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"enum":["pending","confirmed","failed"],"type":"string"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"uploadedBy":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","bucket","storageKey","storageUrl","url","filename","originalFilename","mimeType","sizeBytes","width","height","category","isPublic","status","metadata","uploadedBy","checksum","createdAt","updatedAt"]},{"type":"null"}]},"technologies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"featured":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","slug","lifecycle","visibility","briefDescription","description","heroMetric","imageFileId","imageFile","technologies","link","featured","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/resume":{"get":{"operationId":"v1.resume.get","summary":"Get the canonical resume read-model","description":"Returns the unified resume object built from profile, events, skills, technologies, and social links. Same shape powers /resume.pdf and /resume.yaml.","tags":["Resume"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}]},"summary":{"anyOf":[{"type":"string"},{"type":"null"}]},"contact":{"type":"object","properties":{"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"socials":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"icon":{"type":"string"}},"required":["name","url","icon"]}}},"required":["email","phone","location","socials"]},"experience":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"education":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"certifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"awards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"volunteer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"enum":["employment","education","certification","project","award","volunteer"],"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"company":{"anyOf":[{"type":"string"},{"type":"null"}]},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"link":{"anyOf":[{"type":"string"},{"type":"null"}]},"startDate":{"type":"string","format":"date-time"},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationYears":{"type":"number"},"current":{"type":"boolean"},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description"]}}},"required":["id","type","title","description","company","location","link","startDate","endDate","durationYears","current","skills"]}},"skills":{"type":"object","properties":{"programmingLanguages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"yearsOfExperience":{"type":"number"}},"required":["name","yearsOfExperience"]}},"technologies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"yearsOfExperience":{"type":"number"}},"required":["name","yearsOfExperience"]}},"cloudPlatforms":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"yearsOfExperience":{"type":"number"}},"required":["name","yearsOfExperience"]}}},"required":["programmingLanguages","technologies","cloudPlatforms"]},"languages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"proficiency":{"enum":["native","fluent","conversational","basic"],"type":"string"}},"required":["name","proficiency"]}},"generatedAt":{"type":"string","format":"date-time"}},"required":["name","headline","summary","contact","experience","projects","education","certifications","awards","volunteer","skills","languages","generatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/skills":{"get":{"operationId":"v1.skills.list","summary":"List skills","description":"Returns every skill attached to the resume timeline; each entry carries the `eventId` of the employment or education event it belongs to. For the curated homepage highlights use `/featured-skills` instead.","tags":["Skills"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","eventId","name","description","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/skills/{id}":{"get":{"operationId":"v1.skills.getById","summary":"Get skill by id","description":"Returns a single skill with its parent `eventId`, given an id from `list`. Responds 404 for unknown ids.","tags":["Skills"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","eventId","name","description","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/social-links":{"get":{"operationId":"v1.socialLinks.list","summary":"List social links","description":"Returns the site owner's social and external profiles (GitHub, LinkedIn, etc.) with a name, URL, icon identifier, and `displayOrder` for rendering.","tags":["Social Links"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"url":{"type":"string"},"icon":{"type":"string"},"displayOrder":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","url","icon","displayOrder","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/social-links/{id}":{"get":{"operationId":"v1.socialLinks.getById","summary":"Get social link by id","description":"Returns a single social-link entry, given an id from `list`. Responds 404 for unknown ids.","tags":["Social Links"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"url":{"type":"string"},"icon":{"type":"string"},"displayOrder":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","url","icon","displayOrder","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tags":{"get":{"operationId":"v1.tags.list","summary":"List tags","description":"Returns every writing tag with its slug and display name. Tag slugs appear on posts returned by `/posts` and can be used to group or filter them client-side.","tags":["Tags"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","slug","name","createdAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/technologies":{"get":{"operationId":"v1.technologies.list","summary":"List technologies","description":"Returns the technologies in the toolbox, each with a category, experience level, icon, and URL. Filter with the optional `category` query parameter (language/framework/platform/database/tool/infrastructure/ai).","tags":["Technologies"],"parameters":[{"name":"category","in":"query","schema":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/technologies/featured":{"get":{"operationId":"v1.technologies.getFeatured","summary":"List featured technologies","description":"Returns only the technologies flagged as featured for homepage display. A subset of `list`.","tags":["Technologies"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/technologies/{id}":{"get":{"operationId":"v1.technologies.getById","summary":"Get a technology by id","description":"Returns a single technology entry, given an id from `list`. Responds 404 for unknown ids.","tags":["Technologies"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"category":{"enum":["language","framework","platform","database","tool","infrastructure","ai"],"type":"string"},"experienceLevel":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"featured":{"type":"boolean"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","icon","category","experienceLevel","featured","url","createdAt","updatedAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/wordle":{"get":{"operationId":"v1.wordle.getHistory","summary":"List wordle records","description":"Returns every recorded Wordle game with the answer word, win/loss, guess count, time taken, letter-accuracy tallies, and optional player name. Use `getById` with a record id for the turn-by-turn replay.","tags":["Wordle"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"word":{"type":"string"},"correct":{"type":"boolean"},"guesses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"timeTaken":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"playerName":{"anyOf":[{"type":"string"},{"type":"null"}]},"totalChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"greenChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"yellowChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"}},"required":["id","word","correct","guesses","timeTaken","playerName","totalChars","greenChars","yellowChars","createdAt"]}}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"v1.wordle.createRecord","summary":"Create wordle record","description":"Persists a completed game's result — answer word, win/loss, guess count (1-6), time taken, and letter tallies — and returns the stored record with its id.","tags":["Wordle"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"word":{"type":"string","minLength":5,"maxLength":5},"correct":{"type":"boolean"},"guesses":{"type":"integer","minimum":1,"maximum":6},"timeTaken":{"type":"integer","minimum":0,"maximum":9007199254740991},"playerName":{"anyOf":[{"type":"string"},{"type":"null"}]},"totalChars":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"greenChars":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"yellowChars":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0}},"required":["word","correct","guesses","timeTaken"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"word":{"type":"string"},"correct":{"type":"boolean"},"guesses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"timeTaken":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"playerName":{"anyOf":[{"type":"string"},{"type":"null"}]},"totalChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"greenChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"yellowChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"}},"required":["id","word","correct","guesses","timeTaken","playerName","totalChars","greenChars","yellowChars","createdAt"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/wordle/stats":{"get":{"operationId":"v1.wordle.getStats","summary":"Get wordle stats","description":"Returns aggregate statistics across all recorded games: totals, win rate, current and longest streaks, average guesses and time, best time, and the guess distribution.","tags":["Wordle"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"totalGames":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"totalWins":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"totalLosses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"winRate":{"type":"number"},"currentStreak":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"longestStreak":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"averageGuesses":{"type":"number"},"averageTime":{"type":"number"},"bestTime":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"guessDistribution":{"type":"object","properties":{"1":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"2":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"3":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"4":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"5":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"6":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["1","2","3","4","5","6"]}},"required":["totalGames","totalWins","totalLosses","winRate","currentStreak","longestStreak","averageGuesses","averageTime","bestTime","guessDistribution"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/wordle/random":{"get":{"operationId":"v1.wordle.getRandomWord","summary":"Get random wordle word","description":"Returns a random five-letter answer word from the game's word list to start a new round. Nothing is recorded until the game is submitted via `createRecord`.","tags":["Wordle"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"word":{"type":"string","minLength":5,"maxLength":5}},"required":["word"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/wordle/validate/{word}":{"get":{"operationId":"v1.wordle.validateWord","summary":"Validate wordle word","description":"Checks whether a five-letter guess is in the accepted word list, returning `valid`. Call before scoring a guess so invalid words can be rejected.","tags":["Wordle"],"parameters":[{"name":"word","in":"path","required":true,"schema":{"type":"string","minLength":5,"maxLength":5}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"}},"required":["valid"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/wordle/{id}":{"get":{"operationId":"v1.wordle.getById","summary":"Get wordle replay by id","description":"Returns a finished game's full replay: the record plus its turns in order, given a record id from `getHistory` or `createRecord`.","tags":["Wordle"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID identifier"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"record":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"word":{"type":"string"},"correct":{"type":"boolean"},"guesses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"timeTaken":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"playerName":{"anyOf":[{"type":"string"},{"type":"null"}]},"totalChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"greenChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"yellowChars":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"}},"required":["id","word","correct","guesses","timeTaken","playerName","totalChars","greenChars","yellowChars","createdAt"]},"turns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"turnNumber":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"guess":{"type":"string"},"result":{"type":"string"},"timestamp":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"}},"required":["id","turnNumber","guess","result","timestamp","createdAt"]}}},"required":["record","turns"]}}}},"401":{"description":"UNAUTHORIZED — 401","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN — 403","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND — 404","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"CONFLICT — 409","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"INPUT_VALIDATION_FAILED — 422","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"TOO_MANY_REQUESTS — 429","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"servers":[{"url":"https://api.bokendell.com"}]}