{
  "openapi": "3.0.3",
  "info": {
    "description": "API description",
    "title": "API generated by thrift-gen-http-swagger",
    "version": "0.0.1"
  },
  "paths": {
    "/site_open_api/v1/access_group/list": {
      "post": {
        "operationId": "SiteOpenService_ListAccessGroups",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListAccessGroupsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/capabilities/get": {
      "post": {
        "operationId": "SiteOpenService_GetCapabilities",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetCapabilitiesRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/feed/featured": {
      "post": {
        "operationId": "SiteOpenService_ListFeaturedFeed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListFeaturedFeedReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeaturedSiteFeedRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/feed/list": {
      "post": {
        "operationId": "SiteOpenService_ListFeed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListFeedReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteFeedListRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/feed/top": {
      "post": {
        "operationId": "SiteOpenService_ListTopFeed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListTopFeedReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteFeedListTopRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/media/get_signed_urls": {
      "post": {
        "operationId": "SiteOpenService_GetMediaSignedURLs",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMediaSignedURLsReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMediaSignedURLsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/media/get_token": {
      "post": {
        "operationId": "SiteOpenService_GetMediaToken",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMediaTokenReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaFileTokenRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/member/get": {
      "post": {
        "operationId": "SiteOpenService_GetMember",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMemberReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetMemberRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/member/invite": {
      "post": {
        "operationId": "SiteOpenService_InviteMember",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InviteMemberReqBody"
              }
            }
          },
          "description": "InviteMemberReq invites someone to join the community as a member.\n// There is no role field: an invitation's role is applied when it is\naccepted, so the open API only ever invites members."
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteMemberRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/member/lookup": {
      "post": {
        "operationId": "SiteOpenService_LookupMember",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LookupMemberReqBody"
              }
            }
          },
          "description": "LookupMemberReq resolves an address you already hold to a member id.\n// Exact match only, and deliberately no fuzzy/prefix form: this is an existence\noracle for any address the caller cares to try, so it stays as close to\n\"confirm what I already know\" as possible."
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LookupMemberRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/batch_get": {
      "post": {
        "operationId": "SiteOpenService_BatchGetPosts",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchGetPostsReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchGetPostsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/create": {
      "post": {
        "operationId": "SiteOpenService_CreatePost",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePostReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPostRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/delete": {
      "post": {
        "operationId": "SiteOpenService_DeletePost",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeletePostReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BaseMsgRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/direct_replies/list": {
      "post": {
        "operationId": "SiteOpenService_ListDirectReplies",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListDirectRepliesReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListDirectRepliesRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/featured_posts": {
      "post": {
        "operationId": "SiteOpenService_FeaturedPosts",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeaturedPostsReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeaturedPostsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/get": {
      "post": {
        "operationId": "SiteOpenService_GetPost",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetPostReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPostRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/replies/list": {
      "post": {
        "operationId": "SiteOpenService_ListPostReplies",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListPostRepliesReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPostRepliesRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/reply_descendants/list": {
      "post": {
        "operationId": "SiteOpenService_ListReplyDescendants",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListReplyDescendantsReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListReplyDescendantsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/search": {
      "post": {
        "operationId": "SiteOpenService_SearchPosts",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchPostsReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchPostsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/thread_chain": {
      "post": {
        "operationId": "SiteOpenService_GetThreadChain",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetThreadChainReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetThreadChainRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/post/update": {
      "post": {
        "operationId": "SiteOpenService_UpdatePost",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePostReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BaseMsgRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/site/get": {
      "post": {
        "operationId": "SiteOpenService_GetSite",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteInfoWithBasicSettingsRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/space/feed/list": {
      "post": {
        "operationId": "SiteOpenService_ListSpaceFeed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListSpaceFeedReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpaceFeedListRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/space/feed/top": {
      "post": {
        "operationId": "SiteOpenService_ListTopSpaceFeed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListTopSpaceFeedReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpaceFeedListTopRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/space/get": {
      "post": {
        "operationId": "SiteOpenService_GetSpace",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetSpaceReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetSpaceRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    },
    "/site_open_api/v1/space/list": {
      "post": {
        "operationId": "SiteOpenService_ListSpaces",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListSpacesReqBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListSpacesRespBody"
                }
              }
            },
            "description": "Successful response"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            },
            "description": "Error. The HTTP status follows the platform ErrCode; the body shape is identical for every error."
          }
        },
        "tags": [
          "SiteOpenService"
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ActivityCountInfo": {
        "properties": {
          "favs": {
            "format": "int64",
            "type": "integer"
          },
          "likes": {
            "format": "int64",
            "type": "integer"
          },
          "quotes": {
            "format": "int64",
            "type": "integer"
          },
          "reactions": {
            "description": "Reactions breakdown; likes == Σ reactions[].count. docs_new/post_reactions.",
            "items": {
              "$ref": "#/components/schemas/PostReactionInfo"
            },
            "type": "array"
          },
          "replies": {
            "format": "int64",
            "type": "integer"
          },
          "reposts": {
            "format": "int64",
            "type": "integer"
          },
          "views": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ActivityInfo": {
        "properties": {
          "accepted_answer_id": {
            "type": "string"
          },
          "answers_count": {
            "format": "int64",
            "type": "integer"
          },
          "body": {
            "type": "string"
          },
          "can_view_quoted_post": {
            "type": "boolean"
          },
          "content_locked": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "edited_at": {
            "type": "string"
          },
          "first_replies": {
            "items": {
              "$ref": "#/components/schemas/BaseActivityInfo"
            },
            "type": "array"
          },
          "has_media": {
            "type": "boolean"
          },
          "has_more": {
            "type": "boolean"
          },
          "has_more_replies": {
            "type": "boolean"
          },
          "hidden_reply_count": {
            "format": "int32",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "interaction_type": {
            "type": "string"
          },
          "is_accepted_answer": {
            "type": "boolean"
          },
          "is_featured": {
            "type": "boolean"
          },
          "is_hidden": {
            "description": "Admin moderation state (docs_new/admin_post_hide_lock_review_20260902 §B1).\nis_hidden can only ever be true for the AUTHOR of the post: every other\nviewer gets not_found for a hidden row, so clients may render the\n\"hidden, only you can see this\" notice off this flag directly.\nis_hidden_reason / is_locked_reason are moderator-written copy that IS\nshown to the author — never treat them as internal notes.\ncan_edit is already false while hidden; do not recompute it client-side.",
            "type": "boolean"
          },
          "is_hidden_reason": {
            "type": "string"
          },
          "is_locked": {
            "description": "Moderator post-lock (post_lock_reply_enforcement). Frozen reply tree:\nwhen true, only site admins / space moderators can add new replies.",
            "type": "boolean"
          },
          "is_locked_reason": {
            "type": "string"
          },
          "is_pinned": {
            "type": "boolean"
          },
          "is_scheduled": {
            "type": "boolean"
          },
          "is_sensitive": {
            "description": "Content warning (18-composer.md §4).",
            "type": "boolean"
          },
          "likes_count": {
            "format": "int64",
            "type": "integer"
          },
          "locked_space_id": {
            "type": "string"
          },
          "mentions": {
            "items": {
              "$ref": "#/components/schemas/MentionInfo"
            },
            "type": "array"
          },
          "object_type": {
            "type": "string"
          },
          "operations": {
            "$ref": "#/components/schemas/ActivityOperations"
          },
          "post_attachments": {
            "items": {
              "$ref": "#/components/schemas/PostAttachment"
            },
            "type": "array"
          },
          "post_audio": {
            "$ref": "#/components/schemas/PostAudio"
          },
          "post_blog": {
            "$ref": "#/components/schemas/PostBlog"
          },
          "post_event": {
            "$ref": "#/components/schemas/PostEvent"
          },
          "post_gif": {
            "$ref": "#/components/schemas/PostGif"
          },
          "post_media_files": {
            "items": {
              "$ref": "#/components/schemas/PostMediaFile"
            },
            "type": "array"
          },
          "post_oembed": {
            "$ref": "#/components/schemas/PostOembed"
          },
          "post_poll": {
            "$ref": "#/components/schemas/PostPoll"
          },
          "qa_status": {
            "description": "Q\u0026A fields (only present when space_type = QA)",
            "type": "string"
          },
          "quote_id": {
            "type": "string"
          },
          "quote_post": {
            "$ref": "#/components/schemas/BaseActivityInfo"
          },
          "quoted_post_restricted": {
            "type": "boolean"
          },
          "ranking": {
            "format": "double",
            "type": "number"
          },
          "replies_count": {
            "format": "int64",
            "type": "integer"
          },
          "reply_id": {
            "type": "string"
          },
          "reply_post": {
            "$ref": "#/components/schemas/BaseActivityInfoWithQuote"
          },
          "reply_root_id": {
            "type": "string"
          },
          "reply_root_post": {
            "$ref": "#/components/schemas/BaseActivityInfoWithQuote"
          },
          "repost_id": {
            "type": "string"
          },
          "repost_post": {
            "$ref": "#/components/schemas/BaseActivityInfoWithQuote"
          },
          "restriction_reason": {
            "type": "string"
          },
          "review_status": {
            "description": "Publish outcome, filled by post/create, blog/create, blog/update and\nblog/draft/publish (site_space_admin_review_20260802 B1), and only when\nthe content did NOT publish immediately: a space with\nrequire_post_approval on (or auto-moderation flagging the content) stores\nit with review_status=\"pending\" and writes no activity row yet, and a\nscheduled item publishes later. Those cases return a data object carrying\njust the id, so without an explicit marker the composer cannot tell\n\"published\" from \"waiting for review\" from \"scheduled\". Absent on the\nnormal publish path — including on blog/update, which on the happy path\nre-reads and returns the full article instead.\n// review_status is never \"rejected\" here. Rejection is an admin action that\nalso soft-deletes the row, so the follow-up edit that would report it\nfails with RecordNotFoundErr first. A client may still treat \"rejected\"\nas \"not live\" defensively; nothing emits it today.\n// post/update does NOT use this struct — see UpdatePostData, which carries\nthe same two signals as required fields.",
            "type": "string"
          },
          "scheduled_at": {
            "type": "string"
          },
          "sensitive_reason": {
            "type": "string"
          },
          "site_id": {
            "type": "string"
          },
          "space": {
            "$ref": "#/components/schemas/SpaceBrief"
          },
          "stats": {
            "$ref": "#/components/schemas/ActivityCountInfo"
          },
          "status": {
            "description": "Post status + scheduled_at (18-composer.md §2.3).",
            "type": "string"
          },
          "teaser_excerpt": {
            "description": "Teaser fields for preview mode",
            "type": "string"
          },
          "teaser_has_more": {
            "type": "boolean"
          },
          "teaser_media_count": {
            "format": "int32",
            "type": "integer"
          },
          "thread": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PostThreadInfo"
              }
            ],
            "description": "Thread info (18-composer.md §5.2). Only populated for posts that were\ncreated via post/thread/create or whose root advertises a thread."
          },
          "thread_depth": {
            "format": "int32",
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "user": {
            "$ref": "#/components/schemas/ActivityUserInfo"
          },
          "user_id": {
            "type": "string"
          },
          "verb": {
            "type": "string"
          },
          "views_count": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ActivityOperations": {
        "properties": {
          "actor_role": {
            "description": "Highest-precedence role the viewer has for this post:\nowner / site_admin / space_admin / space_moderator / member / guest.\nTelemetry + delete-endpoint routing on the frontend; see §4.8.",
            "type": "string"
          },
          "can_delete": {
            "description": "owner OR site_admin OR space.can_moderate",
            "type": "boolean"
          },
          "can_edit": {
            "description": "owner + non-rejected + non-hidden + status in {draft,scheduled,published}",
            "type": "boolean"
          },
          "can_feature": {
            "description": "site_admin only, on published top-level posts",
            "type": "boolean"
          },
          "can_moderate": {
            "description": "site_admin OR space.can_moderate (NOT owner)",
            "type": "boolean"
          },
          "can_pin": {
            "description": "depends on pin_context (§4.5); false unless caller passed pin_context",
            "type": "boolean"
          },
          "can_reply": {
            "description": "May the viewer reply to / quote / repost this post. Folds together the\nthread-root interaction_type gate (Everyone / Following / Mentioned /\nNobody), posts.is_locked, and the space's allow_member_reply, so the\nclient no longer calls /post/check_interaction once per rendered post.\nAuthor and site admins are always true. Absent for anonymous viewers on\nolder clients; treat absent as false.",
            "type": "boolean"
          },
          "can_report": {
            "description": "logged-in non-owner non-site-admin on non-deleted post",
            "type": "boolean"
          },
          "favorited": {
            "type": "boolean"
          },
          "is_owner": {
            "description": "post_permissions/01 §3.1 — per-viewer permission bits. All optional so\nthe field is omitted entirely for unauthenticated callers in older\nclients that have not yet rolled out the server-side gating path.",
            "type": "boolean"
          },
          "liked": {
            "type": "boolean"
          },
          "my_reaction": {
            "description": "Reactions (docs_new/post_reactions): the viewer's current emoji. \"\" / omitted\n= no reaction. liked == (my_reaction != \"\"). QnA keeps liked ⟺ upvoted.",
            "type": "string"
          },
          "reposted": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ActivityUserInfo": {
        "properties": {
          "admin_role": {
            "type": "string"
          },
          "avatar": {
            "description": "avatar — RAW storage key; do NOT use in \u003cimg src\u003e. Use avatar_url.",
            "type": "string"
          },
          "avatar_url": {
            "description": "Media URL field (populated at read time)\navatar_url — signed CDN URL, ready for \u003cimg src\u003e.",
            "type": "string"
          },
          "bio": {
            "type": "string"
          },
          "current_badge": {
            "$ref": "#/components/schemas/GamificationBadgeInfo"
          },
          "current_level": {
            "$ref": "#/components/schemas/GamificationLevelInfo"
          },
          "current_member_tier": {
            "$ref": "#/components/schemas/MemberTierBadgeInfo"
          },
          "display_name": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "is_deleted": {
            "type": "boolean"
          },
          "is_verified": {
            "type": "boolean"
          },
          "member_tags": {
            "items": {
              "$ref": "#/components/schemas/MemberTagInfo"
            },
            "type": "array"
          },
          "username": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ApiErrorResponse": {
        "description": "Standard error envelope (API_ERROR_SPEC §0). `code` is the platform ErrCode enum — NOT the HTTP status — and the same code always carries the same localized `msg`.",
        "properties": {
          "code": {
            "description": "Platform ErrCode (e.g. 10004 permission denied, 20103 workspace access version conflict, 20105 workspace ownership transfer required).",
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "additionalProperties": true,
            "description": "Machine-readable detail. Always carries `error_code` (snake_case); capability denials add `required_capability`, `scope_type`, `scope_id`; 20105 (workspace_ownership_transfer_required) adds `workspace_count`, `member_count`, `site_count` — decimal strings, counted across every workspace the caller owns that still holds members or communities.",
            "properties": {
              "error_code": {
                "type": "string"
              },
              "member_count": {
                "description": "20105 only: active members summed across those workspaces.",
                "type": "string"
              },
              "required_capability": {
                "type": "string"
              },
              "scope_id": {
                "type": "string"
              },
              "scope_type": {
                "type": "string"
              },
              "site_count": {
                "description": "20105 only: communities summed across those workspaces; excludes ones already pending purge.",
                "type": "string"
              },
              "workspace_count": {
                "description": "20105 only: workspaces the caller owns that still hold members or communities.",
                "type": "string"
              }
            },
            "type": "object"
          },
          "msg": {
            "description": "Localized, stable per (code, language).",
            "type": "string"
          },
          "request_id": {
            "description": "Correlation id; quote it in support tickets.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "msg"
        ],
        "type": "object"
      },
      "AuthStatSite": {
        "properties": {
          "label": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "AuthTestimonialSite": {
        "properties": {
          "avatar": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "quote": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BaseActivityInfo": {
        "properties": {
          "accepted_answer_id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "can_view_quoted_post": {
            "type": "boolean"
          },
          "content_locked": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "edited_at": {
            "type": "string"
          },
          "has_media": {
            "type": "boolean"
          },
          "has_more": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "interaction_type": {
            "type": "string"
          },
          "is_accepted_answer": {
            "type": "boolean"
          },
          "is_featured": {
            "type": "boolean"
          },
          "is_locked": {
            "description": "Reply-tree freeze flag, mirroring ActivityInfo.is_locked. Carried here so\na nested body can render the \"comments are closed\" state without the\nclient re-fetching the post.",
            "type": "boolean"
          },
          "is_locked_reason": {
            "description": "Why the reply tree is frozen, paired with is_locked above\n(admin_post_hide_lock_review_20260902 §B2). Empty when not locked.\nDeliberately NO is_hidden here: a nested body (quote / reply / root) can\nbelong to somebody else, and the hidden flag is the author's own private\nstate. It is exposed on ActivityInfo only, which the read path already\nguarantees is author-only for hidden rows.",
            "type": "string"
          },
          "is_sensitive": {
            "description": "Content warning (18-composer.md §4). is_sensitive controls feed spoiler\noverlay; sensitive_reason is the optional caption (\u003c=120 chars).",
            "type": "boolean"
          },
          "locked_space_id": {
            "type": "string"
          },
          "mentions": {
            "items": {
              "$ref": "#/components/schemas/MentionInfo"
            },
            "type": "array"
          },
          "object_type": {
            "type": "string"
          },
          "operations": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ActivityOperations"
              }
            ],
            "description": "post_permissions/01 §3.2 — per-viewer permission bits for the nested\n(quote/repost) post body. Optional; omitted on legacy callers."
          },
          "post_attachments": {
            "items": {
              "$ref": "#/components/schemas/PostAttachment"
            },
            "type": "array"
          },
          "post_audio": {
            "$ref": "#/components/schemas/PostAudio"
          },
          "post_blog": {
            "$ref": "#/components/schemas/PostBlog"
          },
          "post_event": {
            "$ref": "#/components/schemas/PostEvent"
          },
          "post_gif": {
            "$ref": "#/components/schemas/PostGif"
          },
          "post_media_files": {
            "items": {
              "$ref": "#/components/schemas/PostMediaFile"
            },
            "type": "array"
          },
          "post_oembed": {
            "$ref": "#/components/schemas/PostOembed"
          },
          "post_poll": {
            "$ref": "#/components/schemas/PostPoll"
          },
          "qa_status": {
            "description": "Q\u0026A fields (only present when space_type = QA)",
            "type": "string"
          },
          "quote_id": {
            "type": "string"
          },
          "quoted_post_restricted": {
            "type": "boolean"
          },
          "ranking": {
            "format": "double",
            "type": "number"
          },
          "reply_id": {
            "type": "string"
          },
          "reply_root_id": {
            "type": "string"
          },
          "repost_id": {
            "type": "string"
          },
          "restriction_reason": {
            "type": "string"
          },
          "scheduled_at": {
            "type": "string"
          },
          "sensitive_reason": {
            "type": "string"
          },
          "site_id": {
            "type": "string"
          },
          "space": {
            "$ref": "#/components/schemas/SpaceBrief"
          },
          "stats": {
            "$ref": "#/components/schemas/ActivityCountInfo"
          },
          "status": {
            "description": "Post status (18-composer.md §2.3): draft | scheduled | published | archived | deleted.",
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "user": {
            "$ref": "#/components/schemas/ActivityUserInfo"
          },
          "user_id": {
            "type": "string"
          },
          "verb": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BaseActivityInfoWithQuote": {
        "properties": {
          "accepted_answer_id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "can_view_quoted_post": {
            "type": "boolean"
          },
          "content_locked": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "edited_at": {
            "type": "string"
          },
          "first_replies": {
            "items": {
              "$ref": "#/components/schemas/BaseActivityInfo"
            },
            "type": "array"
          },
          "has_media": {
            "type": "boolean"
          },
          "has_more": {
            "type": "boolean"
          },
          "has_more_replies": {
            "type": "boolean"
          },
          "hidden_reply_count": {
            "format": "int32",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "interaction_type": {
            "type": "string"
          },
          "is_accepted_answer": {
            "type": "boolean"
          },
          "is_locked": {
            "description": "Reply-tree freeze flag, mirroring ActivityInfo.is_locked.",
            "type": "boolean"
          },
          "is_locked_reason": {
            "description": "Lock reason on a nested body; see BaseActivityInfo for why is_hidden is\nnot mirrored here.",
            "type": "string"
          },
          "is_sensitive": {
            "description": "Content warning (18-composer.md §4).",
            "type": "boolean"
          },
          "locked_space_id": {
            "type": "string"
          },
          "mentions": {
            "items": {
              "$ref": "#/components/schemas/MentionInfo"
            },
            "type": "array"
          },
          "object_type": {
            "type": "string"
          },
          "operations": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ActivityOperations"
              }
            ],
            "description": "post_permissions/01 §3.2 — per-viewer permission bits for the nested\n(reply/repost/root) post body. Optional; omitted on legacy callers."
          },
          "post_attachments": {
            "items": {
              "$ref": "#/components/schemas/PostAttachment"
            },
            "type": "array"
          },
          "post_audio": {
            "$ref": "#/components/schemas/PostAudio"
          },
          "post_blog": {
            "$ref": "#/components/schemas/PostBlog"
          },
          "post_event": {
            "$ref": "#/components/schemas/PostEvent"
          },
          "post_gif": {
            "$ref": "#/components/schemas/PostGif"
          },
          "post_media_files": {
            "items": {
              "$ref": "#/components/schemas/PostMediaFile"
            },
            "type": "array"
          },
          "post_oembed": {
            "$ref": "#/components/schemas/PostOembed"
          },
          "post_poll": {
            "$ref": "#/components/schemas/PostPoll"
          },
          "qa_status": {
            "description": "Q\u0026A fields (only present when space_type = QA)",
            "type": "string"
          },
          "quote_id": {
            "type": "string"
          },
          "quote_post": {
            "$ref": "#/components/schemas/BaseActivityInfo"
          },
          "quoted_post_restricted": {
            "type": "boolean"
          },
          "ranking": {
            "format": "double",
            "type": "number"
          },
          "reply_id": {
            "type": "string"
          },
          "reply_root_id": {
            "type": "string"
          },
          "repost_id": {
            "type": "string"
          },
          "restriction_reason": {
            "type": "string"
          },
          "scheduled_at": {
            "type": "string"
          },
          "sensitive_reason": {
            "type": "string"
          },
          "site_id": {
            "type": "string"
          },
          "space": {
            "$ref": "#/components/schemas/SpaceBrief"
          },
          "stats": {
            "$ref": "#/components/schemas/ActivityCountInfo"
          },
          "status": {
            "type": "string"
          },
          "thread_depth": {
            "format": "int32",
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "user": {
            "$ref": "#/components/schemas/ActivityUserInfo"
          },
          "user_id": {
            "type": "string"
          },
          "verb": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BaseMsgRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BatchGetPostsData": {
        "properties": {
          "missing_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "BatchGetPostsReqBody": {
        "properties": {
          "ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BatchGetPostsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/BatchGetPostsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ContentSettingsInfo": {
        "description": "Content settings for space templates (Gallery requires media, Radio requires audio)",
        "properties": {
          "require_audio": {
            "type": "boolean"
          },
          "require_media": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "CreatePostAudioReq": {
        "properties": {
          "artist": {
            "type": "string"
          },
          "cover": {
            "type": "string"
          },
          "file": {
            "type": "string"
          },
          "kind": {
            "description": "\"track\" (uploaded work, default when omitted) or \"voice\" (recorded message)",
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePostGifReq": {
        "properties": {
          "giphy_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePostMediaItem": {
        "description": "Structured media payload for create/update (18-composer.md §1.1). Send\nEITHER media_ids (legacy, no alt text) OR medias on a given request — not\nboth. `alt` is optional; empty string means explicitly clear; unset means\n\"leave existing alt untouched\" (relevant for update paths).",
        "properties": {
          "alt": {
            "type": "string"
          },
          "media_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePostOembedReq": {
        "properties": {
          "description": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "nonce_str": {
            "type": "string"
          },
          "sig_issued_at": {
            "description": "RFC3339 UTC 签发时间与站点 hashid,均按返回的字符串原文参与签名。",
            "type": "string"
          },
          "sig_site_id": {
            "type": "string"
          },
          "sign": {
            "type": "string"
          },
          "sign_v2": {
            "description": "v2 signature (HMAC-SHA256) covering all OG fields + sig_issued_at +\nsig_site_id. When sign_v2 is present it is verified in preference to the\nlegacy MD5 `sign`; during migration either is accepted.",
            "type": "string"
          },
          "site_name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "youtube_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePostPollReq": {
        "description": "A poll is all-or-nothing: option_1, option_2 and expire_at are required\ntogether. Send the block with every field empty when there is no poll (that\nis what the composer does when the author never opens the poll editor); fill\nin any one of them and the missing ones come back as field errors rather than\nbeing dropped.",
        "properties": {
          "allow_multiple_selection": {
            "description": "Settings: only 3 core settings (poll_type, max_selections, show_results_before_vote, show_voter_list removed)",
            "type": "boolean"
          },
          "allow_vote_change": {
            "type": "boolean"
          },
          "expire_at": {
            "description": "How long the poll runs, as a DURATION BUCKET counted from the moment the\npost is created — one of 1h / 6h / 12h / 1d / 3d / 7d / 14d / 30d. It is\nnot a timestamp: an ISO-8601 date or an epoch is rejected, and there is no\n\"runs forever\" poll (post_polls.expire_at is NOT NULL).\nNote the response side spells the same name differently: PostPoll.expire_at\nis the resolved deadline, formatted as a timestamp.",
            "type": "string"
          },
          "is_anonymous": {
            "type": "boolean"
          },
          "option_1": {
            "type": "string"
          },
          "option_2": {
            "type": "string"
          },
          "option_3": {
            "type": "string"
          },
          "option_4": {
            "type": "string"
          },
          "option_5": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePostReqBody": {
        "properties": {
          "attachment_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "audio": {
            "$ref": "#/components/schemas/CreatePostAudioReq"
          },
          "author_user_id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "gif": {
            "$ref": "#/components/schemas/CreatePostGifReq"
          },
          "idempotency_key": {
            "type": "string"
          },
          "interaction_type": {
            "type": "string"
          },
          "is_sensitive": {
            "description": "Content warning.",
            "type": "boolean"
          },
          "media_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "medias": {
            "description": "Structured media with alt text. Supersedes media_ids; sending both is\nrejected with 10005 by the shared create path.",
            "items": {
              "$ref": "#/components/schemas/CreatePostMediaItem"
            },
            "type": "array"
          },
          "oembed": {
            "$ref": "#/components/schemas/CreatePostOembedReq"
          },
          "poll": {
            "$ref": "#/components/schemas/CreatePostPollReq"
          },
          "quote_id": {
            "type": "string"
          },
          "reply_id": {
            "type": "string"
          },
          "scheduled_at": {
            "format": "int64",
            "type": "integer"
          },
          "sensitive_reason": {
            "type": "string"
          },
          "space_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "topic_ids": {
            "description": "Topic binding. Topics must belong to space_id.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "DeletePostReqBody": {
        "properties": {
          "actor_user_id": {
            "type": "string"
          },
          "idempotency_key": {
            "type": "string"
          },
          "post_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "FeaturedPostsData": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "has_more": {
            "type": "boolean"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "FeaturedPostsReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "space_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "FeaturedPostsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/FeaturedPostsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "FeaturedSiteFeedData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "FeaturedSiteFeedRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/FeaturedSiteFeedData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GamificationBadgeInfo": {
        "properties": {
          "color": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "tier": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GamificationLevelInfo": {
        "properties": {
          "color": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "min_points": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetCapabilitiesData": {
        "description": "---------------------------------------------------------------------------\nC batch: resource discovery\n---------------------------------------------------------------------------",
        "properties": {
          "api_access": {
            "description": "Whether the site's plan includes API access. Omitted when it could not be\ndetermined; absent is not \"denied\".",
            "type": "boolean"
          },
          "api_version": {
            "description": "Matches the path prefix (\"v1\"). There is no separate contract-version\nscheme yet; this exists so one can be introduced without a breaking\nresponse change.",
            "type": "string"
          },
          "quota_limit": {
            "description": "Daily request budget. Omitted together when it could not be read.\n// Describes the counter that actually produces 22204: every call that gets\npast key validation spends one, on every endpoint. Asking for it here\ndoes NOT spend one — this endpoint is exempt from the reservation, and\nthe value is read without incrementing.\n// limit -1 means unlimited. reset_at is RFC3339.",
            "format": "int64",
            "type": "integer"
          },
          "quota_reset_at": {
            "type": "string"
          },
          "quota_used": {
            "format": "int64",
            "type": "integer"
          },
          "scopes": {
            "description": "The scopes THIS key carries, already expanded — never a bare \"*\".",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "site_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetCapabilitiesRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/GetCapabilitiesData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetMediaSignedURLsData": {
        "properties": {
          "urls": {
            "items": {
              "$ref": "#/components/schemas/SignedURLResultData"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "GetMediaSignedURLsReqBody": {
        "properties": {
          "access_level": {
            "enum": [
              "Public",
              "SemiPrivate",
              "Private"
            ],
            "format": "enum",
            "type": "string"
          },
          "author_user_id": {
            "type": "string"
          },
          "is_direct_message": {
            "type": "boolean"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/MediaSignedURLItem"
            },
            "type": "array"
          },
          "space_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetMediaSignedURLsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/GetMediaSignedURLsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetMediaTokenReqBody": {
        "properties": {
          "author_user_id": {
            "type": "string"
          },
          "media_context": {
            "type": "string"
          },
          "scope": {
            "description": "What the token will be used for. Omitted or empty means \"upload\".\n// Note this differs from site/media_file.thrift MediaFileTokenReq, where an\nabsent scope mints upload+read+delete. Widest-by-default is wrong for a\ncredential handed to a third-party integration, and it also used to skip\nthe grace-period plan gate, which only fires for upload-scoped requests.",
            "type": "string"
          },
          "space_id": {
            "description": "The course's space id (hashid), required for a non-site-admin course\nmanager to be recognized when media_context = \"course_video\".",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetMemberData": {
        "properties": {
          "member": {
            "$ref": "#/components/schemas/OpenMember"
          }
        },
        "type": "object"
      },
      "GetMemberReqBody": {
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetMemberRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/GetMemberData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetPostReqBody": {
        "properties": {
          "id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetPostRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ActivityInfo"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetSpaceReqBody": {
        "properties": {
          "id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetSpaceRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SpaceInfo"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetThreadChainData": {
        "properties": {
          "has_more": {
            "type": "boolean"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          },
          "total_depth": {
            "format": "int32",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "GetThreadChainReqBody": {
        "properties": {
          "from_level": {
            "format": "int32",
            "type": "integer"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "post_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "GetThreadChainRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/GetThreadChainData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "InviteMemberData": {
        "properties": {
          "accept_url": {
            "description": "Present only for a send_email=false invitation that is still pending.\nDeliberately absent for emailed ones: the emailed link doubles as proof\nthat the recipient owns the inbox, which holds only while nobody but the\ninbox has it.",
            "type": "string"
          },
          "email_sent": {
            "description": "Meaningful for emailed invitations only. false = the invitation exists\nbut its email could not be queued; send it again with a NEW\nidempotency_key and force_refresh=true.",
            "type": "boolean"
          },
          "expires_at": {
            "description": "ISO 8601. Invitations are valid for 7 days.",
            "type": "string"
          },
          "invitation_id": {
            "type": "string"
          },
          "status": {
            "description": "pending on a fresh call. A replay reports the invitation as it is now:\naccepted, expired or revoked are possible.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "InviteMemberReqBody": {
        "properties": {
          "access_group_ids": {
            "description": "Access groups granted when the invitation is accepted — ids from\n/access_group/list. Duplicates are collapsed; an id that is not a live\ngroup of this community is 10005 / access_group_not_found, with\ndata.invalid_ids naming them. A group deleted before acceptance is\nskipped then.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "custom_message": {
            "description": "Shown in the email and on the invitation landing page (link-only\ninvitations too).",
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "force_refresh": {
            "description": "When a pending invitation for this address already exists:\nomitted / false → 23306, data.invitation_id names it. To refresh it,\nresend with force_refresh=true and a NEW\nidempotency_key (the body changed);\ntrue            → refresh it in place: new token (the old link stops\nworking), new expiry, and this request's groups,\nmessage, inviter and delivery replace the old ones.\nMember tags an admin attached are kept.\nAn invitation an admin issued with a role other than member is never\nrefreshed: 23306 either way.",
            "type": "boolean"
          },
          "idempotency_key": {
            "description": "Scoped to (community, this endpoint). A retry with the same key and the\nsame body returns the same invitation and sends nothing; the same key\nwith a different body is rejected with 10005. A refused request frees\nits key only for that same body: changing anything — e.g. adding\nforce_refresh after a 23306 — is a new request and needs a new key.",
            "type": "string"
          },
          "inviter_user_id": {
            "description": "Member the invitation is sent as (email copy, audit, grant attribution).\nMust be an ACTIVE member of this community holding the members.invite\ncapability — 20303 if unknown or inactive, 10004 / inviter_not_permitted\nwithout the capability. Omitted = the community owner.",
            "type": "string"
          },
          "send_email": {
            "description": "Omitted or true: email the invitation. false: send nothing and return\naccept_url for the caller to deliver itself. Signing up through such a\nlink on an invite-only community still requires the email code — the\nlink was not proven to reach the invited inbox.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "InviteMemberRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/InviteMemberData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListAccessGroupsData": {
        "properties": {
          "access_groups": {
            "description": "In the admin's drag order. Not paginated: a community has a handful.",
            "items": {
              "$ref": "#/components/schemas/OpenAccessGroupInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ListAccessGroupsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ListAccessGroupsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListDirectRepliesData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ListDirectRepliesReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "post_id": {
            "type": "string"
          },
          "sort": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListDirectRepliesRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ListDirectRepliesData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListFeaturedFeedReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListFeedReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "since_id": {
            "description": "Pull only what is newer than a post you already have. Mirrors\nsite/site_feed.thrift SiteFeedReq.since_id.\n// NOT an incremental sync cursor: it surfaces new posts only, never edits\nor deletions, so a consumer polling it will silently drift from the\nsource. Use it to top up a timeline, not to mirror one.",
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListPostRepliesData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ListPostRepliesReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "post_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListPostRepliesRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ListPostRepliesData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListReplyDescendantsData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ListReplyDescendantsReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "post_id": {
            "type": "string"
          },
          "sort": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListReplyDescendantsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ListReplyDescendantsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListSpaceFeedReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "qa_filter": {
            "description": "Fields 5-8 mirror site/space_feed.thrift SpaceFeedReq one for one; keep\nthe validators byte-identical so the two surfaces cannot disagree about\nwhich combinations are legal.",
            "type": "string"
          },
          "qa_sort": {
            "type": "string"
          },
          "search": {
            "type": "string"
          },
          "sort": {
            "description": "\"newest\" (default) = reverse chronological; \"top\" = platform ranking.\nIgnored in QA spaces, which order by qa_sort instead.\n// `cursor` encoding follows `sort`, so a cursor minted under one sort is\nnot valid under another — reset paging when it changes.",
            "type": "string"
          },
          "space_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListSpacesData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "spaces": {
            "items": {
              "$ref": "#/components/schemas/SpaceInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ListSpacesReqBody": {
        "properties": {
          "cursor": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "search_query": {
            "type": "string"
          },
          "viewer_user_id": {
            "description": "Read the catalogue as this member: which spaces are visible depends on\nmembership. Omitted = anonymous, i.e. publicly readable spaces only.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListSpacesRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/ListSpacesData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListTopFeedReqBody": {
        "properties": {
          "cursor": {
            "format": "double",
            "type": "number"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ListTopSpaceFeedReqBody": {
        "properties": {
          "cursor": {
            "format": "double",
            "type": "number"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "space_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "LookupMemberData": {
        "properties": {
          "found": {
            "type": "boolean"
          },
          "member": {
            "allOf": [
              {
                "$ref": "#/components/schemas/OpenMember"
              }
            ],
            "description": "Absent when no member matches. A miss is a normal result, not an error, so\nhit and miss cost the caller the same."
          }
        },
        "type": "object"
      },
      "LookupMemberReqBody": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "LookupMemberRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/LookupMemberData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MediaFileTokenInfo": {
        "properties": {
          "expires_at": {
            "type": "string"
          },
          "token": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MediaFileTokenRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/MediaFileTokenInfo"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MediaSignedURLItem": {
        "properties": {
          "file_cate": {
            "enum": [
              "Media",
              "Avatar",
              "Header",
              "Audio",
              "File"
            ],
            "format": "enum",
            "type": "string"
          },
          "file_ext": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          },
          "file_type": {
            "enum": [
              "Image",
              "Video",
              "Audio",
              "File"
            ],
            "format": "enum",
            "type": "string"
          },
          "media_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MemberTagInfo": {
        "properties": {
          "color": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MemberTierBadgeInfo": {
        "properties": {
          "color": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "MentionInfo": {
        "properties": {
          "avatar_url": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "end_position": {
            "format": "int32",
            "type": "integer"
          },
          "start_position": {
            "format": "int32",
            "type": "integer"
          },
          "user_id": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "OpenAccessGroup": {
        "description": "OpenAccessGroup is an access group as the community itself shows it: the\nsame id / name / color any member sees on another member's profile.\n// Nothing about HOW the member got it (subscription, manual grant, invitation,\nsite default): that reveals paid / comped status and stays admin-only.",
        "properties": {
          "color": {
            "description": "Hex like \"#6B5BFF\"; empty when the admin never set one.",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "OpenAccessGroupInfo": {
        "description": "OpenAccessGroupInfo is one entry of the community's access-group catalogue —\nenough to pick ids for /member/invite. No member counts, bound plans or\nunlocked spaces: operating data, not what naming a group needs.",
        "properties": {
          "color": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "is_default": {
            "description": "New members are placed in this group automatically when they sign up.",
            "type": "boolean"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "OpenMember": {
        "description": "OpenMember is a deliberately narrow projection of the member record.\n// NOT base.User: that carries email, custom profile fields and counters that a\nserver-side integration has no standing claim to. Fields are added here one at\na time, with a reason — the default is that a field is absent.",
        "properties": {
          "access_groups": {
            "description": "Every access group the member holds, in the admin's drag order, so\nelement 0 is the primary group (the one shown as the member's badge).\nAlways present: [] means the member holds none. Never \"unknown\" — if the\ngroups cannot be read, the whole request fails instead.",
            "items": {
              "$ref": "#/components/schemas/OpenAccessGroup"
            },
            "type": "array"
          },
          "avatar_url": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "role": {
            "description": "\"owner\" / \"admin\" / \"moderator\" / \"member\". Resolved server-side; owner is\nderived from is_creator, not from the admin_role column, which has no\nowner value.",
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PendingAccessRequestInfo": {
        "properties": {
          "id": {
            "type": "string"
          },
          "requested_at": {
            "type": "string"
          },
          "reviewed_at": {
            "type": "string"
          },
          "reviewer_message": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostAttachment": {
        "properties": {
          "content_type": {
            "type": "string"
          },
          "file_ext": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          },
          "file_size": {
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "url": {
            "description": "Signed URL field (populated when Cloudflare signing is enabled)",
            "type": "string"
          },
          "url_expires_at": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostAudio": {
        "properties": {
          "artist": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "cover": {
            "type": "string"
          },
          "cover_url": {
            "type": "string"
          },
          "duration": {
            "format": "double",
            "type": "number"
          },
          "file": {
            "type": "string"
          },
          "file_ext": {
            "type": "string"
          },
          "file_url": {
            "description": "Signed URL fields (populated when Cloudflare signing is enabled)",
            "type": "string"
          },
          "kind": {
            "description": "\"track\" | \"voice\"; pre-kind data reads back as \"track\". The vd matters on\nthe admin surface, where this struct doubles as a request body\n(PostAdminDraftUpdatePatch.audio).",
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url_expires_at": {
            "type": "string"
          },
          "waveform_url": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostBlog": {
        "properties": {
          "cover": {
            "type": "string"
          },
          "cover_url": {
            "description": "Media URL field",
            "type": "string"
          },
          "meta_description": {
            "type": "string"
          },
          "meta_title": {
            "description": "SEO meta fields",
            "type": "string"
          },
          "og_image": {
            "type": "string"
          },
          "og_image_url": {
            "description": "Signed CDN URL built from og_image when it is a media hash ID. Empty when\nog_image already holds a fully qualified URL, or when og_image is empty.",
            "type": "string"
          },
          "read_time_minutes": {
            "description": "Estimated reading time in minutes",
            "format": "int32",
            "type": "integer"
          },
          "scheduled_at": {
            "description": "ISO 8601 string when status = \"scheduled\".",
            "type": "string"
          },
          "status": {
            "description": "One of draft | scheduled | published | archived.",
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "tags": {
            "description": "Tag slugs associated with this blog (populated at read time from hashtag_posts).",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "PostEvent": {
        "properties": {
          "attendee_count": {
            "format": "int64",
            "type": "integer"
          },
          "cover": {
            "type": "string"
          },
          "cover_url": {
            "description": "Media URL field",
            "type": "string"
          },
          "end_date": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "location_city": {
            "type": "string"
          },
          "location_name": {
            "type": "string"
          },
          "max_attendees": {
            "format": "int64",
            "type": "integer"
          },
          "online_url": {
            "type": "string"
          },
          "start_date": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostGif": {
        "properties": {
          "giphy_id": {
            "type": "string"
          },
          "giphy_url": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostMediaFile": {
        "properties": {
          "alt": {
            "description": "Accessibility alt text (18-composer.md §1). Empty string when legacy\nmedia had no alt; frontend falls back to \"[image]\" for screen readers.",
            "type": "string"
          },
          "aspect": {
            "format": "double",
            "type": "number"
          },
          "blurhash": {
            "type": "string"
          },
          "file_cate": {
            "type": "string"
          },
          "file_ext": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          },
          "file_type": {
            "type": "string"
          },
          "hls_error_msg": {
            "description": "non-empty only when hls_status=\"failed\"",
            "type": "string"
          },
          "hls_status": {
            "description": "Sprint 3 follow-up (P1 #5): HLS lifecycle + moderation state so the\nfrontend can render four-state video players and occlude taken_down\ncontent without a second fetch. Distinct from hashtag.moderation_status\n— the media vocabulary is {approved, reported, taken_down}.",
            "type": "string"
          },
          "hls_url": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "large_url": {
            "type": "string"
          },
          "medium_url": {
            "type": "string"
          },
          "moderation_status": {
            "description": "approved / reported / taken_down",
            "type": "string"
          },
          "playback_error_msg": {
            "description": "non-empty only when playback_status=\"failed\"",
            "type": "string"
          },
          "playback_kind": {
            "description": "\"\" / original / remux_mp4 / preview_mp4",
            "type": "string"
          },
          "playback_status": {
            "description": "Playback quick pass (2026-04-21): browser-playable MP4 fast-path,\ndecoupled from HLS transcoding. Let the player start once\nplayback_status=\"ready\" without waiting for adaptive bitrate.\nplayback_kind=original   → playback_url reuses the original URL\nplayback_kind=remux_mp4  → ffmpeg -c copy output at playback/{id}/playback.mp4\nplayback_kind=preview_mp4 → re-encoded 480p/720p H.264 preview",
            "type": "string"
          },
          "playback_url": {
            "description": "empty unless playback_status=\"ready\"",
            "type": "string"
          },
          "processing_error": {
            "description": "non-empty only when processing_status=\"failed\"",
            "type": "string"
          },
          "processing_status": {
            "description": "Async upload pipeline (2026-04-19): covers upload → thumbnail →\nblurhash generation, independent of hls_status. Frontend renders\nplaceholder while pending/processing; reconcile with status_batch\nsince the embedded value may be stale (set at post-create time).",
            "type": "string"
          },
          "thumb_url": {
            "type": "string"
          },
          "url": {
            "description": "Signed URL fields (populated when Cloudflare signing is enabled)",
            "type": "string"
          },
          "url_expires_at": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostOembed": {
        "properties": {
          "description": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "site_name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "youtube_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostPoll": {
        "properties": {
          "allow_multiple_selection": {
            "type": "boolean"
          },
          "allow_vote_change": {
            "type": "boolean"
          },
          "can_edit": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "expire_at": {
            "description": "Resolved deadline, formatted as a timestamp — NOT the duration bucket\n(\"7d\" etc.) that CreatePostPollReq.expire_at takes on the way in.",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "is_anonymous": {
            "type": "boolean"
          },
          "max_selections": {
            "format": "int32",
            "type": "integer"
          },
          "option_1": {
            "type": "string"
          },
          "option_2": {
            "type": "string"
          },
          "option_3": {
            "type": "string"
          },
          "option_4": {
            "type": "string"
          },
          "option_5": {
            "description": "Option 5 (NEW)",
            "type": "string"
          },
          "poll_type": {
            "description": "Settings (NEW)",
            "type": "string"
          },
          "show_results_before_vote": {
            "type": "boolean"
          },
          "show_voter_list": {
            "type": "boolean"
          },
          "status": {
            "description": "Status (NEW)",
            "type": "string"
          },
          "total_voters": {
            "format": "int64",
            "type": "integer"
          },
          "total_votes": {
            "description": "Aggregations (NEW)",
            "format": "int64",
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          },
          "user_vote": {
            "description": "User's vote",
            "format": "int32",
            "type": "integer"
          },
          "user_votes": {
            "description": "For multi-select",
            "items": {
              "format": "int32",
              "type": "integer"
            },
            "type": "array"
          },
          "votes_1": {
            "format": "int64",
            "type": "integer"
          },
          "votes_2": {
            "format": "int64",
            "type": "integer"
          },
          "votes_3": {
            "format": "int64",
            "type": "integer"
          },
          "votes_4": {
            "format": "int64",
            "type": "integer"
          },
          "votes_5": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PostReactionInfo": {
        "description": "PostReactionInfo — per-emoji breakdown entry (docs_new/post_reactions).\nNamed with the Post prefix to stay globally unique in site_swagger (distinct\nfrom the chat-side MessageReactionData).",
        "properties": {
          "count": {
            "format": "int32",
            "type": "integer"
          },
          "reacted_by_me": {
            "type": "boolean"
          },
          "reaction": {
            "description": "unicode emoji or \"custom:\u003cid\u003e\"",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PostThreadInfo": {
        "description": "Thread navigation info (18-composer.md §5.2). Emitted only for posts that\nbelong to a thread (batch-created via post/thread/create, or whose root\ncarries a thread marker). position is 0-indexed; total is the known size.",
        "properties": {
          "position": {
            "format": "int32",
            "type": "integer"
          },
          "root_post_id": {
            "type": "string"
          },
          "thread_id": {
            "type": "string"
          },
          "total": {
            "format": "int32",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SearchPostsData": {
        "properties": {
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "page": {
            "format": "int32",
            "type": "integer"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          },
          "total_hits": {
            "format": "int64",
            "type": "integer"
          },
          "total_pages": {
            "format": "int32",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SearchPostsReqBody": {
        "properties": {
          "filter_type": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "page": {
            "format": "int32",
            "type": "integer"
          },
          "query": {
            "type": "string"
          },
          "sort": {
            "type": "string"
          },
          "space_id": {
            "type": "string"
          },
          "user_id": {
            "type": "string"
          },
          "viewer_user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SearchPostsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SearchPostsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SidebarWidgetSite": {
        "properties": {
          "content": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "key": {
            "type": "string"
          },
          "position": {
            "format": "int32",
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "description": "\"builtin\" | \"html\"",
            "type": "string"
          },
          "visibility": {
            "description": "\"all\" | \"guest\" | \"member\"",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SignedURLResultData": {
        "properties": {
          "expires_at": {
            "type": "string"
          },
          "hls_url": {
            "description": "HLS streaming URL (populated for video files after transcoding)",
            "type": "string"
          },
          "large_url": {
            "type": "string"
          },
          "media_id": {
            "type": "string"
          },
          "medium_url": {
            "type": "string"
          },
          "original_url": {
            "type": "string"
          },
          "playback_kind": {
            "description": "\"\" / original / remux_mp4 / preview_mp4",
            "type": "string"
          },
          "playback_status": {
            "description": "not_applicable / pending / processing / ready / failed",
            "type": "string"
          },
          "playback_url": {
            "description": "Playback quick pass (2026-04-21): browser-playable MP4 fast-path,\nready before HLS. Empty unless playback_status=\"ready\".",
            "type": "string"
          },
          "thumb_url": {
            "type": "string"
          },
          "waveform_url": {
            "description": "Waveform JSON signed URL (populated for audio files)",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteAppearanceInfo": {
        "properties": {
          "auth_background_image": {
            "description": "§10 Auth pages background image — shared by every page mounted on\nAuthLayout (sign_in / sign_up / forgot_password / reset / 2FA / SSO).\nAnonymous frontends render auth_background_image_url; if it's empty\nthey fall back to the dark monogram + theme spotlight default.",
            "type": "string"
          },
          "auth_background_image_url": {
            "type": "string"
          },
          "auth_stats": {
            "items": {
              "$ref": "#/components/schemas/AuthStatSite"
            },
            "type": "array"
          },
          "auth_testimonials": {
            "description": "§9 Login page social proof — delivered as typed lists so the\nanonymous login/sign-up page doesn't need to parse JSON itself.\nEmpty lists mean \"section hidden\".",
            "items": {
              "$ref": "#/components/schemas/AuthTestimonialSite"
            },
            "type": "array"
          },
          "default_appearance": {
            "type": "string"
          },
          "discover_feed_top_window": {
            "description": "Phase 4 — Discover Top window (\"day\" | \"week\" | \"month\" | \"all\").\nEmpty string indicates never-saved; frontends should treat it as \"week\"\nto match server default, which is also what a legacy \"year\" value reads\nback as. Only meaningful when discover_feed_type == \"Top\".",
            "type": "string"
          },
          "discover_feed_type": {
            "description": "Feed Display",
            "type": "string"
          },
          "favicon": {
            "type": "string"
          },
          "favicon_url": {
            "type": "string"
          },
          "header_logo_mode": {
            "description": "Navigation header logo form — closed enum \"wordmark\" | \"icon_name\" |\n\"icon\" (default \"wordmark\" = historic behavior). wordmark = horizontal\nlogo; icon_name = square logo + site name; icon = square logo only.\nThe public header (HeaderLogo) reads this; unknown/empty fall back to\n\"wordmark\".",
            "type": "string"
          },
          "logo": {
            "description": "Logos \u0026 Branding",
            "type": "string"
          },
          "logo_dark": {
            "type": "string"
          },
          "logo_dark_url": {
            "type": "string"
          },
          "logo_size": {
            "description": "Navigation logo display height — closed enum \"sm\" | \"md\" | \"lg\",\ndefault \"md\" (historic 36px). The public header (HeaderLogo) maps\nsm→28px / md→36px / lg→44px; unknown/empty values fall back to \"md\".",
            "type": "string"
          },
          "logo_url": {
            "description": "Signed URL fields",
            "type": "string"
          },
          "mobile_app_icon": {
            "type": "string"
          },
          "mobile_app_promotion_enabled": {
            "description": "Site-owner opt-in to show members the (Mateflow-branded) mobile app.\nOnly consumed when site.white_label is true — the full gate is\n`!white_label || mobile_app_promotion_enabled`, so non-white-label\nsites keep promoting regardless of this flag and need no backfill.\nReadable by guests: /download is a public page.",
            "type": "boolean"
          },
          "mobile_theme": {
            "description": "Field ID 11 (default_space_layout) removed 2026-06-01 — inert (never\nseeded new spaces; C-end renders per-space space_layout). See\ndocs_new/admin_appearance_interface/backend_requirements.md §1.1.\nMobile Appearance",
            "type": "string"
          },
          "open_graph_image": {
            "type": "string"
          },
          "open_graph_image_url": {
            "type": "string"
          },
          "sidebar_widgets": {
            "description": "Field IDs 40–43 (guest/member landing_page + landing_url) were removed\npre-launch.\nPhase 3 — Sidebar Widgets delivered as typed list so frontends can\nrender without parsing JSON themselves. Custom widget `content` is\nalready sanitized at write time; safe to render.",
            "items": {
              "$ref": "#/components/schemas/SidebarWidgetSite"
            },
            "type": "array"
          },
          "square_logo": {
            "type": "string"
          },
          "square_logo_dark": {
            "type": "string"
          },
          "square_logo_dark_url": {
            "type": "string"
          },
          "square_logo_url": {
            "type": "string"
          },
          "theme_revision": {
            "description": "Theme cache-bust key — monotonically increases whenever the currently\npublished theme is mutated (publish / update_current / revert).\nFrontends append ?v=\u003ctheme_revision\u003e to the public\n/site_api/v1/theme/current.css URL so CDN/browser caches are\ninvalidated atomically without resorting to no-cache headers.",
            "format": "int64",
            "type": "integer"
          },
          "url_expires_at": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteFeedListData": {
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "SiteFeedListRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SiteFeedListData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteFeedListTopData": {
        "properties": {
          "next_cursor": {
            "format": "double",
            "type": "number"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "SiteFeedListTopRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SiteFeedListTopData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteInfo": {
        "properties": {
          "allow_search_engine_indexing": {
            "type": "boolean"
          },
          "complimentary_expires_at": {
            "description": "ISO8601 expiry of a complimentary grant; \"\" = indefinite/permanent. Only\nmeaningful when subscription_status == complimentary（\"\"→「永久免费」, 否则「免费至 {date}」).",
            "type": "string"
          },
          "contact_email": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "creator_id": {
            "type": "string"
          },
          "custom_domain": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "is_private": {
            "type": "boolean"
          },
          "maintenance_message": {
            "type": "string"
          },
          "maintenance_mode": {
            "type": "boolean"
          },
          "max_members": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "stat": {
            "$ref": "#/components/schemas/SiteStatInfo"
          },
          "status": {
            "type": "string"
          },
          "subdomain": {
            "type": "string"
          },
          "subscription_status": {
            "description": "status (field 6) is operational only (active/suspended/pending_purge).\nBilling state lives in subscription_status (trial/expired/complimentary…);\nsuspend_reason explains a suspension. See docs_new/site_status.",
            "type": "string"
          },
          "suspend_reason": {
            "type": "string"
          },
          "tagline": {
            "description": "Brand identity / contact / visibility / maintenance — surfaced from\n/settings/general/{fetch,update}. The public site uses these to render\nthe tagline under site_name, the support email on auth pages, the\nrobots meta / sitemap gating, and the maintenance interstitial.\n// Backend invariant: when is_private == true, allow_search_engine_indexing\nis forced false on the read path regardless of the persisted value.",
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "white_label": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "SiteInfoWithBasicSettingsData": {
        "properties": {
          "site": {
            "$ref": "#/components/schemas/SiteInfo"
          },
          "site_appearance": {
            "$ref": "#/components/schemas/SiteAppearanceInfo"
          },
          "site_setting": {
            "$ref": "#/components/schemas/SiteSettingSimpleInfo"
          }
        },
        "type": "object"
      },
      "SiteInfoWithBasicSettingsRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SiteInfoWithBasicSettingsData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteSettingSimpleInfo": {
        "description": "Field id 2 (password_min_length) retired — never enforced; do NOT reuse.",
        "properties": {
          "allow_mentions": {
            "type": "boolean"
          },
          "cookie_policy_url": {
            "type": "string"
          },
          "default_language": {
            "type": "string"
          },
          "default_timezone": {
            "type": "string"
          },
          "direct_messaging_enabled": {
            "description": "Communication",
            "type": "boolean"
          },
          "enable_turnstile": {
            "description": "allow_registration retired; replaced by SiteRegistrationSetting.mode\n(canonical) on /site_admin_api/v1/settings/registration/fetch. Field\nid 1 left vacant to keep wire-compat predictable.",
            "type": "boolean"
          },
          "enable_two_factor_auth": {
            "description": "Site-level 2FA switch (public, read-only). Source: site_securities, same\nflag as the admin /settings/security/fetch endpoint. false → the member\nsettings page must hide the 2FA section; /user/settings/2fa/enable/init\nand /enable/confirm reject with 22827 in that state. Members who already\nenabled 2FA keep their login challenge / recovery codes / disable path.",
            "type": "boolean"
          },
          "gamification_enabled": {
            "type": "boolean"
          },
          "group_messaging_enabled": {
            "type": "boolean"
          },
          "has_courses": {
            "description": "Lifetime content markers (public, read-only). Source: sites.has_courses /\nsites.has_events. \"Has this site ever created a course / an event\", NOT\n\"does it have one right now\": drafts, archived and deleted ones all count,\nand the flag never falls back to false once set. Site-wide, never filtered\nby the caller's permissions. The member frontend hides the Courses / Events main-nav entries for\nordinary members when these are false, so that a site whose plan enables\nthe feature but which never used it stops shipping two dead links. Admins\nkeep seeing the entries — hiding them from the owner too would remove the\nonly route to the action that flips the flag. Deliberately booleans and\nnot counts: a count would end up on a badge and drag per-viewer visibility\nback in. See docs_new/site_nav_empty_menu_20260908.",
            "type": "boolean"
          },
          "has_events": {
            "type": "boolean"
          },
          "leaderboard_period": {
            "type": "string"
          },
          "leaderboard_size": {
            "format": "int32",
            "type": "integer"
          },
          "leaderboard_visible": {
            "description": "Leaderboard config (public, read-only). Source: site_gamification, same\ndata as the admin /settings/gamification/fetch endpoint. Without these\nthe member FE cannot honour the admin's leaderboard switch/period/size —\nleaderboard_visible=false only emptied the response server-side while the\nwidget kept rendering.\nleaderboard_period: weekly | monthly | all_time (the admin enum; the\nleaderboard endpoints additionally accept daily on time_range).",
            "type": "boolean"
          },
          "minimum_age": {
            "description": "GDPR Art.8 age gate (public, read-only). Source: site_legals, same data\nas /settings/legal/fetch. Lets the signup page render the age\naffirmation / DOB field. minimum_age default 16; require_dob=false =\nlight affirmation checkbox.",
            "format": "int32",
            "type": "integer"
          },
          "points_name": {
            "type": "string"
          },
          "points_reward_icon": {
            "type": "string"
          },
          "privacy_policy_url": {
            "type": "string"
          },
          "reaction_set": {
            "description": "L1 reaction quick-bar set (docs_new/post_reactions §3.7). Always populated\n(empty → default 7). Member FE reads this to render the reaction bar.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "reactions_enabled": {
            "type": "boolean"
          },
          "registration_mode": {
            "description": "Registration mode for the /signup page to branch on. Values:\nopen | invite_only | approval | closed. See addendum §4.",
            "type": "string"
          },
          "require_dob": {
            "type": "boolean"
          },
          "show_rank_on_profile": {
            "type": "boolean"
          },
          "terms_of_service_url": {
            "description": "Legal policy URLs (public, read-only). Source: site_legals table, same\ndata as the admin /settings/legal/fetch endpoint. Lets the footer +\nsignup/login consent notice render Terms / Privacy / Cookie links from\nthe public bootstrap. gdpr_compliance is admin-only and NOT exposed.",
            "type": "string"
          },
          "turnstile_site_key": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteStatInfo": {
        "properties": {
          "blog_count": {
            "format": "int64",
            "type": "integer"
          },
          "event_count": {
            "format": "int64",
            "type": "integer"
          },
          "member_count": {
            "format": "int64",
            "type": "integer"
          },
          "post_count": {
            "format": "int64",
            "type": "integer"
          },
          "space_count": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SpaceAccessInfoItem": {
        "description": "Access info for space list card rendering (API layer)",
        "properties": {
          "access_type": {
            "type": "string"
          },
          "currency": {
            "description": "ISO 4217; empty when unpriced",
            "type": "string"
          },
          "has_access": {
            "type": "boolean"
          },
          "membership_status": {
            "type": "string"
          },
          "price_cents": {
            "description": "CENTS; 0 when unpriced",
            "format": "int64",
            "type": "integer"
          },
          "price_display": {
            "description": "Pre-formatted, and it ALREADY carries the period suffix for subscriptions\n(\"$9.99/month\"); buyout has none (\"$9.99\"). Do NOT append your own cadence —\nthat renders \"$9.99/month/mo\". Two hard limits make it display-only: the\nperiod word is hardcoded English, and the symbol table covers USD/EUR/GBP,\nso every other currency renders with \"$\". Prefer the structured fields below.",
            "type": "string"
          },
          "price_period": {
            "description": "Structured mirror of price_display. Same vocabulary as the course DTOs'\nlowest_tier_period, so both surfaces read alike. Needed on its own when the\ncadence has to be carried rather than printed — e.g. pre-selecting the plan\nthe user was just quoted, instead of re-deriving it by regex.",
            "type": "string"
          },
          "space_access_level": {
            "type": "string"
          },
          "subscription_status": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceArchiveInfo": {
        "properties": {
          "archive_reason": {
            "description": "operator-supplied reason, may be \"\"",
            "type": "string"
          },
          "archived_at": {
            "description": "unix seconds, as string for parity with other timestamps",
            "type": "string"
          },
          "archived_by": {
            "description": "hashid; \"\" when unknown",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceBrief": {
        "description": "SpaceBrief is a compact embedded space reference (mirrors ActivityUserInfo): a\nspace's identity + display media with CDN URLs resolved at read time. Reused\nwherever a response embeds the space a row belongs to.",
        "properties": {
          "avatar": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "icon_url": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "space_type": {
            "description": "Post | Blog | Media | Chat | Podcast | Course | QA.\n// Lets a client apply a space-TYPE rule to an embedded post without a second\nlookup. Reposting is the current case: it does not exist in a QA space at\nall (a type rule, not a permission, so no audience or exemption grants\nit), and the only other available signal was qa_status — which top-level\nquestions carry but ANSWERS DO NOT, so a repost entry point keyed on it\nstill leaks on answers.",
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceContentCategoryInfo": {
        "properties": {
          "count": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceFeedListData": {
        "properties": {
          "is_preview": {
            "description": "Preview mode fields",
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          },
          "preview_limit": {
            "format": "int32",
            "type": "integer"
          },
          "total_hits": {
            "description": "Search-mode only: Meilisearch estimated total matches, for facet counts.\nAbsent (or 0) when `search` was not supplied. Never use it for pagination —\n`next_cursor` non-empty is the only \"has more\" signal.",
            "format": "int64",
            "type": "integer"
          },
          "total_posts_count": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SpaceFeedListRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SpaceFeedListData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceFeedListTopData": {
        "properties": {
          "next_cursor": {
            "format": "double",
            "type": "number"
          },
          "posts": {
            "items": {
              "$ref": "#/components/schemas/ActivityInfo"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "SpaceFeedListTopRespBody": {
        "properties": {
          "code": {
            "format": "int32",
            "type": "integer"
          },
          "data": {
            "$ref": "#/components/schemas/SpaceFeedListTopData"
          },
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceInfo": {
        "properties": {
          "access_info": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpaceAccessInfoItem"
              }
            ],
            "description": "Access info for list rendering"
          },
          "active_members_count": {
            "format": "int64",
            "type": "integer"
          },
          "allow_member_event": {
            "type": "boolean"
          },
          "allow_member_like": {
            "type": "boolean"
          },
          "allow_member_live": {
            "type": "boolean"
          },
          "allow_member_post": {
            "type": "boolean"
          },
          "allow_member_reply": {
            "type": "boolean"
          },
          "allow_member_repost": {
            "type": "boolean"
          },
          "archive_info": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpaceArchiveInfo"
              }
            ],
            "description": "Archive metadata. Present only when the space has been archived;\nfrontend can rely on presence as the canonical \"this is archived\"\nsignal instead of inferring from CanPost=false."
          },
          "avatar": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string"
          },
          "content_settings": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ContentSettingsInfo"
              }
            ],
            "description": "Content settings (for Gallery/Radio space templates)"
          },
          "cover": {
            "type": "string"
          },
          "cover_url": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "default_reply_view": {
            "description": "Default reply display form: \"flat\" | \"tree\". FE reads this as the space\ndefault; per-viewer overrides are handled entirely on the front-end.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "events_enabled": {
            "type": "boolean"
          },
          "featured": {
            "type": "boolean"
          },
          "has_events": {
            "description": "Whether this Space holds any event a member could open, so the tab can be\nrendered (or not) before any event request goes out. Counts past events,\nexcludes cancelled/deleted/unapproved. Absent = \"not computed\" — show the\ntab; only an explicit false means there is nothing to show.",
            "type": "boolean"
          },
          "hide_members_count": {
            "type": "boolean"
          },
          "icon": {
            "type": "string"
          },
          "icon_url": {
            "description": "Media URL fields (populated at read time)",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "live_enabled": {
            "description": "The live module's switches, so a console can render the toggle it writes\nthrough space/permissions/update.",
            "type": "boolean"
          },
          "members_count": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "operations": {
            "$ref": "#/components/schemas/SpaceOperationsInfo"
          },
          "owner_user_id": {
            "description": "The space creator's user id (hashid). operations.is_admin is true for the\nowner, every appointed space admin AND every site admin, so it cannot\nsingle the owner out; space_role on a member row only ever holds\nmember | admin | moderator and has no \"owner\" value either. This is the\nonly field that identifies the one member whom member management refuses\nto act on (space_owner_protected), so a client can grey that row out\ninstead of discovering it from a rejection. Absent when the owner is\nunknown; treat absence as \"cannot tell\", not as \"no owner\".",
            "type": "string"
          },
          "posts_count": {
            "format": "int64",
            "type": "integer"
          },
          "preview_config": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpacePreviewConfigInfo"
              }
            ],
            "description": "Preview configuration"
          },
          "preview_stats": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpacePreviewStatsInfo"
              }
            ],
            "description": "Preview stats (for non-members)"
          },
          "replies_count": {
            "format": "int64",
            "type": "integer"
          },
          "require_event_approval": {
            "description": "Whether member-created events in this Space wait for admin approval.\nThe column and every consumer of it (create → pending, list visibility,\nRSVP gate, the back-office review queue) have existed since Phase 3, but\nno API could ever write it, so the queue could not receive anything\n(docs_new/event_module_review_20260909/06 §6). Exposed here so the space\nsettings console can render the toggle it now writes.",
            "type": "boolean"
          },
          "site_id": {
            "type": "string"
          },
          "space_category_id": {
            "type": "string"
          },
          "space_layout": {
            "type": "string"
          },
          "space_privacy": {
            "type": "string"
          },
          "space_type": {
            "type": "string"
          },
          "subscription_info": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpaceSubscriptionConfigInfo"
              }
            ],
            "description": "Subscription/monetization config"
          },
          "teaser_data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SpaceTeaserDataInfo"
              }
            ],
            "description": "Teaser display data"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceOperationsInfo": {
        "properties": {
          "access_denial_reason": {
            "description": "Enhanced access control fields",
            "type": "string"
          },
          "can_comment": {
            "description": "Granular permissions",
            "type": "boolean"
          },
          "can_create_event": {
            "description": "Event permissions",
            "type": "boolean"
          },
          "can_invite": {
            "type": "boolean"
          },
          "can_join": {
            "description": "---- Access model (see spaces migration 20260808120000) ----\n// can_join is the single signal the join CTA renders from. Do NOT infer it\nfrom space_privacy: a publicly readable space may still admit nobody, and\na member-only one may be open to all.\njoin_policy = open    -\u003e \"Join\"\njoin_policy = request -\u003e \"Request to join\" (can_join=false once pending)\njoin_policy = paid    -\u003e price CTA\njoin_policy = invite  -\u003e render nothing",
            "type": "boolean"
          },
          "can_manage_space": {
            "description": "Whether the viewer may open this space's manage workspace. True exactly when POST /site_api/v1/space/manage/context would succeed for the same viewer with space.manage.view in actor.capabilities — both come from the same backend resolver, so they cannot disagree. This covers site owners/admins who never joined, the space creator (even one demoted to member), appointed space admins and space moderators; it is false for ordinary members, non-members, and banned or muted admins/moderators. Course spaces follow the same rule, but a Course has no moderator workspace, so a space moderator of a Course reads false. Site-wide authority comes only from the site owner/admin role: site moderators and custom site roles, whatever capabilities they carry, read false unless they also hold a space role here. A display hint for the \"Manage space\" entry, not authorization: the workspace re-checks on open. Set by space_category/list_with_spaces and space/favorite/list, always true or false for a signed-in viewer; absent for anonymous viewers and on every other surface — treat absent as \"unknown\", not as false. Unrelated to course/detail's can_manage, which only means \"may read the whole course without enrolling\".",
            "type": "boolean"
          },
          "can_moderate": {
            "type": "boolean"
          },
          "can_post": {
            "type": "boolean"
          },
          "can_preview": {
            "type": "boolean"
          },
          "can_react": {
            "type": "boolean"
          },
          "can_request_access": {
            "type": "boolean"
          },
          "can_view_analytics": {
            "type": "boolean"
          },
          "can_view_members": {
            "type": "boolean"
          },
          "has_access": {
            "type": "boolean"
          },
          "is_admin": {
            "type": "boolean"
          },
          "is_banned": {
            "type": "boolean"
          },
          "is_favorited": {
            "description": "Whether this viewer starred the space. Stored on the same per-user row as\nnotification_level, so both arrive together and neither costs a request\nof its own. Absent for anonymous viewers — a star is a statement about a\nperson. Written through POST /site_api/v1/space/favorite/update.",
            "type": "boolean"
          },
          "is_member": {
            "type": "boolean"
          },
          "is_moderator": {
            "type": "boolean"
          },
          "is_muted": {
            "type": "boolean"
          },
          "join_policy": {
            "description": "open | request | paid | invite",
            "type": "string"
          },
          "membership_status": {
            "type": "string"
          },
          "mute_until": {
            "description": "Mute expiry for the current user — ISO8601 string. Absent for permanent\nmute or when not muted.",
            "type": "string"
          },
          "notification_level": {
            "description": "Current user's per-space notification level: all / highlights /\nmentions_only / off. Set only for authenticated viewers; defaults to\n\"all\" when the user hasn't customized it — the neutral level, which\ndefers to the global per-category notification settings. \"highlights\"\nis an explicit choice and suppresses likes and reposts only —\nreplies, quotes, @mentions, accepted answers and course updates all\nstill deliver; \"mentions_only\" also silences course updates. There is\nno new-post stream to suppress: that notification type has no producer\nand is not planned. The levels gate space-scoped notifications only —\nevent reminders/updates (you RSVP'd) and moderation verdicts on your\nown posts are never gated by them. Distinct from is_muted, which is a\nmoderation (silenced-by-admin) flag, not a notification pref.",
            "type": "string"
          },
          "pending_access_request": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PendingAccessRequestInfo"
              }
            ],
            "description": "Pending access request"
          },
          "permission_level": {
            "format": "int32",
            "type": "integer"
          },
          "post_permission": {
            "description": "viewers | members | staff — who may start a topic here.",
            "type": "string"
          },
          "reply_permission": {
            "description": "viewers | members | staff — who may reply, like or repost here.",
            "type": "string"
          },
          "space_access_level": {
            "description": "Space access level (canonical rendering level)",
            "type": "string"
          },
          "subscription_status": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpacePreviewConfigInfo": {
        "description": "Tier comparison item (API layer)\nPreview configuration for a space",
        "properties": {
          "blur_last_post": {
            "type": "boolean"
          },
          "custom_unlock_message": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "post_limit": {
            "format": "int32",
            "type": "integer"
          },
          "show_engagement_stats": {
            "type": "boolean"
          },
          "show_member_count": {
            "type": "boolean"
          },
          "show_post_count": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "SpacePreviewStatsInfo": {
        "description": "Preview stats for non-member display",
        "properties": {
          "active_discussions": {
            "format": "int64",
            "type": "integer"
          },
          "content_categories": {
            "items": {
              "$ref": "#/components/schemas/SpaceContentCategoryInfo"
            },
            "type": "array"
          },
          "recent_highlights": {
            "items": {
              "$ref": "#/components/schemas/SpaceRecentHighlightInfo"
            },
            "type": "array"
          },
          "top_contributors": {
            "items": {
              "$ref": "#/components/schemas/SpaceTopContributorInfo"
            },
            "type": "array"
          },
          "total_members": {
            "format": "int64",
            "type": "integer"
          },
          "total_posts": {
            "format": "int64",
            "type": "integer"
          },
          "weekly_new_posts": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SpaceRecentHighlightInfo": {
        "properties": {
          "comments_count": {
            "format": "int64",
            "type": "integer"
          },
          "likes_count": {
            "format": "int64",
            "type": "integer"
          },
          "post_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceSubscriptionConfigInfo": {
        "description": "Subscription/monetization configuration for a space (API layer)",
        "properties": {
          "access_type": {
            "description": "free, subscription, one_time, subscription_or_one_time",
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "monthly_price": {
            "description": "cents",
            "format": "int64",
            "type": "integer"
          },
          "one_time_price": {
            "description": "cents",
            "format": "int64",
            "type": "integer"
          },
          "preview_enabled": {
            "type": "boolean"
          },
          "preview_post_limit": {
            "format": "int32",
            "type": "integer"
          },
          "price_display": {
            "description": "pre-formatted buyout/subscription price; mirrors SpaceAccessInfoItem.price_display so teaser/locked views never hand-build \"$\"",
            "type": "string"
          },
          "required_tier_id": {
            "type": "string"
          },
          "required_tier_name": {
            "type": "string"
          },
          "trial_days": {
            "format": "int32",
            "type": "integer"
          },
          "yearly_price": {
            "description": "cents",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SpaceTeaserDataInfo": {
        "description": "Teaser display data for a space",
        "properties": {
          "top_tags": {
            "items": {
              "$ref": "#/components/schemas/SpaceContentCategoryInfo"
            },
            "type": "array"
          },
          "total_members": {
            "format": "int64",
            "type": "integer"
          },
          "total_posts": {
            "format": "int64",
            "type": "integer"
          },
          "weekly_activity": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SpaceTopContributorInfo": {
        "properties": {
          "avatar": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "post_count": {
            "format": "int64",
            "type": "integer"
          },
          "user_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdatePostReqBody": {
        "properties": {
          "actor_user_id": {
            "type": "string"
          },
          "attachment_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "audio": {
            "$ref": "#/components/schemas/CreatePostAudioReq"
          },
          "body": {
            "description": "Omit to keep the text. Sent (even as \"\") in a request that carries the\npost's content (poll, media, ...), it replaces the text, so \"\" clears\nit. On a metadata-only edit an empty body is ignored.",
            "type": "string"
          },
          "gif": {
            "$ref": "#/components/schemas/CreatePostGifReq"
          },
          "idempotency_key": {
            "type": "string"
          },
          "interaction_type": {
            "type": "string"
          },
          "is_sensitive": {
            "description": "Content warning.",
            "type": "boolean"
          },
          "media_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "medias": {
            "description": "Structured media with alt text. Supersedes media_ids; sending both is\nrejected with 10005 by the shared update path.",
            "items": {
              "$ref": "#/components/schemas/CreatePostMediaItem"
            },
            "type": "array"
          },
          "oembed": {
            "$ref": "#/components/schemas/CreatePostOembedReq"
          },
          "poll": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreatePostPollReq"
              }
            ],
            "description": "Omit to keep the poll unchanged; an edit cannot add or remove one. Only\na poll nobody has voted on and that has not ended can change — otherwise\nthe block is ignored and the call still succeeds. expire_at is a duration\ntier: the new deadline is now + tier, or, while the post is still\nscheduled, the poll's start + tier (its clock starts at publish)."
          },
          "post_id": {
            "type": "string"
          },
          "quote_id": {
            "type": "string"
          },
          "reply_id": {
            "type": "string"
          },
          "scheduled_at": {
            "description": "Reschedule, or cancel a pending schedule.\n// Unix seconds, matching CreatePostReq.scheduled_at — deliberately NOT the\nRFC3339 string the member API uses here, so the two open-API post writes\nagree with each other. Omit the field to leave the schedule untouched.\n// \u003c= 0 cancels the schedule and returns the post to draft — but ONLY blog\nposts have a draft state to return to. Cancelling on a plain post is\nrefused with 10005 / schedule_cancel_unsupported rather than accepted,\nbecause the underlying behaviour there is \"publish immediately\": an\nintegration asking to cancel would instead make the content public early,\nfan it out to the feed and fire notifications, none of which a follow-up\ncall can undo.",
            "format": "int64",
            "type": "integer"
          },
          "sensitive_reason": {
            "type": "string"
          },
          "space_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "topic_ids": {
            "description": "Topic rebinding. Mirrors create.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "version": {
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "description": "Site API key. Takes precedence when both credentials are sent.",
        "in": "header",
        "name": "X-API-Key",
        "type": "apiKey"
      },
      "BearerAuth": {
        "description": "The same site API key, sent as `Authorization: Bearer \u003ckey\u003e`.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "SiteOpenService"
    }
  ]
}