{"openapi":"3.1.0","info":{"title":"Mug Club Public API","version":"1.0.0","summary":"Public read endpoints for member profiles, membership aggregates, and mug metadata.","description":"This is the public contract for Mug Club API endpoints. Current unversioned routes are governed as v1 semantics.","contact":{"name":"Mug Club Support","url":"https://discord.gg/ZsPcBzmeyh"}},"servers":[{"url":"https://mugclub.wtf"}],"tags":[{"name":"Profiles","description":"Authenticated profile + membership endpoints."},{"name":"Members","description":"Public-safe user lookups by handle or wallet address."},{"name":"Mugs","description":"Parsed mug metadata collection endpoint."}],"paths":{"/api/me":{"get":{"tags":["Profiles"],"summary":"Get authenticated aggregate profile and membership","description":"Requires x-wallet-address header. Aggregates DB-backed social profile and onchain membership.","parameters":[{"name":"x-wallet-address","in":"header","required":true,"schema":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$"}}],"responses":{"200":{"description":"Authenticated aggregate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserAggregateResponse"}}}},"401":{"description":"Missing or invalid wallet header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream RPC provider error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/me/profile":{"get":{"tags":["Profiles"],"summary":"Get authenticated social profile","parameters":[{"name":"x-wallet-address","in":"header","required":true,"schema":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$"}}],"responses":{"200":{"description":"Authenticated profile","content":{"application/json":{"schema":{"type":"object","properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/MeProfile"},{"type":"null"}]},"detectedEnsAlias":{"type":["string","null"]}},"required":["profile","detectedEnsAlias"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid wallet header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/me/membership":{"get":{"tags":["Profiles"],"summary":"Get authenticated onchain membership","parameters":[{"name":"x-wallet-address","in":"header","required":true,"schema":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$"}}],"responses":{"200":{"description":"Authenticated membership","content":{"application/json":{"schema":{"type":"object","properties":{"membership":{"$ref":"#/components/schemas/MembershipResponse"}},"required":["membership"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid wallet header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream RPC provider error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/{id}":{"get":{"tags":["Members"],"summary":"Get public-safe aggregate profile and membership for a user","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Handle or wallet address."}],"responses":{"200":{"description":"Public-safe aggregate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUserAggregateResponse"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream RPC provider error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/{id}/profile":{"get":{"tags":["Members"],"summary":"Get public-safe social profile for a user","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Handle or wallet address."}],"responses":{"200":{"description":"Public-safe profile","content":{"application/json":{"schema":{"type":"object","properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/PublicMemberProfile"},{"type":"null"}]}},"required":["profile"],"additionalProperties":false}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/{id}/membership":{"get":{"tags":["Members"],"summary":"Get onchain membership for a user","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Handle or wallet address."}],"responses":{"200":{"description":"Membership lookup","content":{"application/json":{"schema":{"type":"object","properties":{"membership":{"anyOf":[{"$ref":"#/components/schemas/MembershipResponse"},{"type":"null"}]}},"required":["membership"],"additionalProperties":false}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream RPC provider error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/members/{handle}":{"get":{"tags":["Members"],"summary":"Get a public member profile by handle","description":"Handle is normalized before lookup (trimmed, @ removed, lowercased). Returns only public profile fields.","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"},"description":"Member handle, with or without @ prefix."}],"responses":{"200":{"description":"Public member profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicMemberProfile"}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"memberNotFound":{"value":{"error":"Member not found"}}}}}}}}},"/api/mugs":{"get":{"tags":["Mugs"],"summary":"List parsed mug metadata","description":"Returns only valid mug metadata records. Null/invalid upstream metadata entries are filtered out before response.","responses":{"200":{"description":"Mug metadata list","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, s-maxage=300, stale-while-revalidate=900"}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MugMetadata"}}}}}}}}},"components":{"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false},"ProfileToken":{"type":"object","properties":{"tokenId":{"type":"string"},"balance":{"type":"string"},"hasToken":{"type":"boolean"}},"required":["tokenId","balance","hasToken"],"additionalProperties":false},"ProfileResponse":{"type":"object","properties":{"address":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$"},"contract":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$"},"chainId":{"type":"integer","const":8453},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/ProfileToken"}},"totalBalance":{"type":"string"},"hasAnyToken":{"type":"boolean"}},"required":["address","contract","chainId","tokens","totalBalance","hasAnyToken"],"additionalProperties":false},"PublicMemberProfile":{"type":"object","properties":{"handle":{"type":"string"},"primaryAlias":{"type":"string"},"ensAlias":{"type":["string","null"]},"displayName":{"type":["string","null"]},"bio":{"type":["string","null"]},"websiteUrl":{"type":["string","null"]}},"required":["handle","primaryAlias","ensAlias","displayName","bio","websiteUrl"],"additionalProperties":false},"MeProfile":{"type":"object","properties":{"walletAddress":{"type":["string","null"],"pattern":"^0x[a-fA-F0-9]{40}$"},"handle":{"type":"string"},"primaryAlias":{"type":"string"},"ensAlias":{"type":["string","null"]},"displayName":{"type":["string","null"]},"bio":{"type":["string","null"]},"websiteUrl":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["walletAddress","handle","primaryAlias","ensAlias","displayName","bio","websiteUrl","createdAt","updatedAt"],"additionalProperties":false},"MembershipResponse":{"allOf":[{"$ref":"#/components/schemas/ProfileResponse"},{"type":"object","properties":{"checkedAt":{"type":"string","format":"date-time"},"blockNumber":{"type":"string"}},"required":["checkedAt"],"additionalProperties":false}]},"UserAggregateResponse":{"type":"object","properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/MeProfile"},{"type":"null"}]},"membership":{"$ref":"#/components/schemas/MembershipResponse"}},"required":["profile","membership"],"additionalProperties":false},"PublicUserAggregateResponse":{"type":"object","properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/PublicMemberProfile"},{"type":"null"}]},"membership":{"anyOf":[{"$ref":"#/components/schemas/MembershipResponse"},{"type":"null"}]}},"required":["profile","membership"],"additionalProperties":false},"MugAttribute":{"type":"object","properties":{"trait_type":{"type":"string"},"value":{"type":"string"}},"required":["trait_type","value"],"additionalProperties":false},"ImageDetails":{"type":"object","properties":{"bytes":{"type":"number"},"format":{"type":"string"},"height":{"type":"number"},"width":{"type":"number"}},"required":["bytes","format","height","width"],"additionalProperties":false},"AnimationDetails":{"type":"object","properties":{"bytes":{"type":"number"},"format":{"type":"string"}},"required":["bytes","format"],"additionalProperties":false},"MugMetadata":{"type":"object","properties":{"tokenId":{"type":"number"},"name":{"type":"string"},"description":{"type":"string"},"image":{"type":"string"},"image_url":{"type":"string"},"animation_url":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/MugAttribute"}},"image_details":{"$ref":"#/components/schemas/ImageDetails"},"animation_details":{"$ref":"#/components/schemas/AnimationDetails"}},"required":["tokenId","name","description","image","image_url","animation_url","attributes","image_details","animation_details"],"additionalProperties":false}}},"x-mugclub-policies":{"versioning":"Current public endpoints are treated as v1 contract semantics. Breaking changes require a new versioned path and updated OpenAPI version.","rateLimiting":"Clients should implement retries with exponential backoff for transient 5xx responses from upstream RPC dependencies.","corsAllowlisting":"Browser Origin policies may be enforced via ALLOWED_CORS_ORIGINS depending on deployment configuration.","support":"Integration support is provided via the Mug Club Discord community."}}