{
  "schema_version": "2.0",
  "project": "go-threads-api",
  "contract_version": "0.5.0",
  "protocol": "grpc",
  "account_authentication": {
    "type": "request_message",
    "field": "account_state",
    "value_format": "threads.auth.v1.AccountState",
    "not_required_for": [
      "threads.auth.v1.AuthService/Login"
    ],
    "notes": "包含账号 uid、完整设备、Session、可选 Proxy 和 App 版本。"
  },
  "server_authentication": {
    "type": "grpc_metadata",
    "key": "x-threads-api-key",
    "required_when": "Go server 配置 THREADS_GRPC_API_KEY 时"
  },
  "request_routing": {
    "type": "request_message",
    "field": "account_state.proxy",
    "value_format": "threads.auth.v1.Proxy",
    "sensitive": true,
    "notes": "可选；Python 与 Proto 均使用 Proxy 强类型对象；账号密码成对可选，省略时支持 IP 白名单代理。"
  },
  "python_sdk": {
    "package": "threads-sdk",
    "import": "threads_sdk",
    "client": "threads_sdk.ThreadsClient",
    "version_source": "release_tag",
    "compatible_server": "必须连接同一 X.Y.Z Tag 发布的 Go gRPC server",
    "release_assets": [
      "threads_sdk-X.Y.Z-py3-none-any.whl",
      "threads_sdk-X.Y.Z.tar.gz",
      "threads_sdk-X.Y.Z-copy.zip"
    ],
    "public_downloads": {
      "production_latest": "https://threads-api.es007.com/latest/docs/sdk-download/",
      "testing_latest": "https://threads-api.es007.com/test/latest/docs/sdk-download/",
      "versioned_pattern": "https://threads-api.es007.com/{channel}/{X.Y.Z}/docs/sdk-download/"
    },
    "direct_copy": "解压 copy.zip 后把完整 threads_sdk/ 复制进项目；内部已包含 protobuf/gRPC stub",
    "configuration": {
      "server_target": "ThreadsClient(target=...)",
      "default_target": "127.0.0.1:50051",
      "tls": "跨主机设置 tls=True；私有 CA 通过 root_certificates 提供"
    }
  },
  "modules": [
    {
      "id": "auth",
      "title": "登录与当前账号",
      "client": "client.auth（仅登录）/ account.auth（登录后）",
      "purpose": "登录、校验会话并读取当前账号身份。",
      "order": 1
    },
    {
      "id": "profile",
      "title": "资料与主页帖子",
      "client": "account.profile",
      "purpose": "读取用户资料、保存当前账号资料并分页采集主页帖子。",
      "order": 2
    },
    {
      "id": "posts",
      "title": "帖子发布与删除",
      "client": "account.posts",
      "purpose": "检查文本、发布文本帖、上传图片、发布单图帖和删除帖子。",
      "order": 3
    },
    {
      "id": "replies",
      "title": "回复采集",
      "client": "account.replies",
      "purpose": "分页读取指定账号的回复列表。",
      "order": 4
    },
    {
      "id": "search",
      "title": "搜索与主题校验",
      "client": "account.search",
      "purpose": "关键词搜索以及发帖主题标签校验。",
      "order": 5
    },
    {
      "id": "feed",
      "title": "推荐用户",
      "client": "account.feed",
      "purpose": "分页读取 Threads 推荐账号。",
      "order": 6
    },
    {
      "id": "friendships",
      "title": "关系状态",
      "client": "account.friendships",
      "purpose": "读取与目标账号之间的关注、拉黑和静音状态。",
      "order": 7
    }
  ],
  "workflows": [
    {
      "id": "verify_session",
      "title": "校验已保存的完整账号状态",
      "module": "auth",
      "status": "available",
      "goal": "确认 AccountState 对应的真实账号，并取得后续调用使用的资料字段。",
      "prerequisites": [
        "已从使用者存储恢复完整 AccountState。",
        "每个账号状态携带自己的可选 Proxy；不要让多账号共享可变状态对象。"
      ],
      "steps": [
        {
          "rpc": "threads.auth.v1.AuthService/GetCurrentUser",
          "action": "调用 get_current_user(edit=False) 校验会话。",
          "inputs": "先通过 client.account(state) 创建账号客户端，再传 edit=False。",
          "output": "核对 pk、username；任务结束后保存 account.state。"
        }
      ],
      "field_mappings": [],
      "missing_capabilities": []
    },
    {
      "id": "login",
      "title": "账密登录并取得 IGT:2",
      "module": "auth",
      "status": "experimental",
      "goal": "通过 CAA/Bloks 登录生成会话并即时核对账号身份。",
      "prerequisites": [
        "准备账号、密码和可选 TOTP seed；Proxy 可省略。",
        "生产容器已经固定加载仓库版本化 keybox.xml；SDK 用户不传 keybox。",
        "登录成功后由使用者持久化返回的 AccountState。"
      ],
      "steps": [
        {
          "rpc": "threads.auth.v1.AuthService/Login",
          "action": "提交账号、密码、可选 2FA seed 和可选账号代理；Go server 生成首次登录设备。",
          "inputs": "代理可省略；传递 Proxy(host, port, protocol, username, password)。username/password 成对可选，省略时用于 IP 白名单代理；协议支持 HTTP、HTTPS、SOCKS5、SOCKS5H。配置字符串只能先通过 Proxy.from_url() 转成强类型对象。不要把密码、2FA seed 或代理凭据写入日志。",
          "output": "核对 success、account_state、verified_user.username 和 assurance；把 account_state 保存到使用者自己的存储。"
        }
      ],
      "field_mappings": [
        {
          "from": "LoginResponse.account_state",
          "to": "ThreadsClient.account(state)",
          "description": "后续独立任务从使用者存储加载完整状态；SDK 不负责持久化。"
        }
      ],
      "missing_capabilities": [
        "软件环境尚不能证明 session 可长期稳定；stable_for_automation 必须保持 false，直到硬件证明和存活观测通过。"
      ]
    },
    {
      "id": "edit_profile",
      "title": "安全编辑当前账号资料",
      "module": "profile",
      "status": "available",
      "goal": "修改指定资料字段，同时避免整表回传接口清空未携带的原值。",
      "prerequisites": [
        "已从使用者存储恢复完整 AccountState。"
      ],
      "steps": [
        {
          "rpc": "threads.auth.v1.AuthService/GetCurrentUser",
          "action": "先调用 get_current_user(edit=True) 读取完整当前资料。",
          "inputs": "edit=True。",
          "output": "保存 username、full_name、biography、is_private、external_url 和 bio_links。"
        },
        {
          "rpc": "threads.profile.v1.ProfileService/EditProfile",
          "action": "只替换目标字段，其余字段使用上一步原值并整表提交。",
          "inputs": "username/first_name/biography/is_private 必须完整回传；设备身份由 AccountState 内部提供。",
          "output": "核对返回资料，再调用 get_current_user(edit=True) 验证保存结果。"
        }
      ],
      "field_mappings": [
        {
          "from": "CurrentUser.username/full_name/biography/is_private/external_url",
          "to": "EditProfileRequest 对应字段",
          "description": "未修改字段必须回填原值，不能省略。"
        }
      ],
      "missing_capabilities": [
        "头像和封面需要独立上传端点，当前契约未实现。",
        "location 不是 EditProfile 端点字段。"
      ]
    },
    {
      "id": "create_text_post",
      "title": "发布文本帖",
      "module": "posts",
      "status": "available",
      "goal": "使用当前账号和持久设备身份创建纯文本帖子。",
      "prerequisites": [
        "已从登录响应或使用者存储取得完整 AccountState。",
        "写操作已获得明确授权。"
      ],
      "steps": [
        {
          "rpc": "threads.posts.v1.PostsService/CreateTextPost",
          "action": "用 ThreadsClient.account(state).posts 提交 caption 和可选发帖功能字段。",
          "inputs": "AccountState 自动提供 uid、完整设备、Session、代理与 App 版本；纯文本帖不需要先上传媒体。",
          "output": "核对 Media 的作者、caption、code 和 permalink，并把 account.state 的刷新结果写回使用者存储。"
        },
        {
          "rpc": "threads.profile.v1.ProfileService/ListProfileThreads",
          "action": "重新读取账号主页确认帖子真实出现。",
          "inputs": "user_id 使用 AccountState.uid。",
          "output": "在 threads[].items[].post 中找到新帖子。"
        }
      ],
      "field_mappings": [
        {
          "from": "LoginResponse.account_state 或使用者存储",
          "to": "CreateTextPostRequest.account_state",
          "description": "每个独立任务显式传入完整状态；响应状态覆盖保存后供下一任务使用。"
        }
      ],
      "missing_capabilities": []
    },
    {
      "id": "create_image_post",
      "title": "发布单图帖",
      "module": "posts",
      "status": "available",
      "goal": "先上传 WebP 图片，再用上传结果创建单图帖子。",
      "prerequisites": [
        "已从登录响应或使用者存储取得完整 AccountState。",
        "准备 WebP 图片字节及真实宽高。",
        "写操作已获得明确授权。"
      ],
      "steps": [
        {
          "rpc": "threads.posts.v1.PostsService/UploadImage",
          "action": "上传图片二进制及真实宽高。",
          "inputs": "image_data 为 WebP bytes；original_width/original_height 来自图片元数据。",
          "output": "要求 status=ok，并保存响应 upload_id。"
        },
        {
          "rpc": "threads.posts.v1.PostsService/CreateImagePost",
          "action": "使用上传响应创建单图帖。",
          "inputs": "upload_id 必须使用 UploadImageResult.upload_id；宽高与上传步骤保持一致。",
          "output": "核对 media_type=1、caption、image_versions2 和帖子作者。"
        },
        {
          "rpc": "threads.profile.v1.ProfileService/ListProfileThreads",
          "action": "重新读取账号主页确认图片帖真实出现。",
          "inputs": "user_id 使用发帖 uid。",
          "output": "在 threads[].items[].post 中找到新帖子和图片字段。"
        }
      ],
      "field_mappings": [
        {
          "from": "UploadImageResult.upload_id",
          "to": "CreateImagePostRequest.upload_id",
          "description": "图片上传返回值必须原样传给创建接口。"
        },
        {
          "from": "UploadImageRequest.original_width/original_height",
          "to": "CreateImagePostRequest.original_width/original_height",
          "description": "创建接口使用与上传图片一致的真实尺寸。"
        }
      ],
      "missing_capabilities": []
    },
    {
      "id": "create_video_post",
      "title": "发布视频帖",
      "module": "posts",
      "status": "unavailable",
      "goal": "上传视频并创建视频帖子。",
      "prerequisites": [],
      "steps": [],
      "field_mappings": [],
      "missing_capabilities": [
        "当前 Proto、Go SDK 和 Python 门面均没有视频上传 RPC。",
        "当前没有视频 configure RPC、视频转码状态查询或封面上传工作流。",
        "在完成真实抓包、契约和黑盒测试前，禁止复用 UploadImage/CreateImagePost 伪装视频发布。"
      ]
    },
    {
      "id": "collect_profile_threads",
      "title": "分页采集主页帖子",
      "module": "profile",
      "status": "available",
      "goal": "读取指定账号主页帖子，并使用游标持续翻页。",
      "prerequisites": [
        "已从使用者存储恢复完整 AccountState。",
        "准备目标 user_id。"
      ],
      "steps": [
        {
          "rpc": "threads.profile.v1.ProfileService/ListProfileThreads",
          "action": "首次调用不传 max_id，读取 threads 强类型结果。",
          "inputs": "user_id 为目标账号；exclude_reposts 按采集需求设置。",
          "output": "消费 threads[].items[].post，并读取 next_cursor。"
        },
        {
          "rpc": "threads.profile.v1.ProfileService/ListProfileThreads",
          "action": "next_cursor 非空时继续请求下一页。",
          "inputs": "将上一页 next_cursor 传入下一次 max_id。",
          "output": "next_cursor 为空时结束；raw_json 只用于未建模字段排错。"
        }
      ],
      "field_mappings": [
        {
          "from": "ProfileThreadsPage.next_cursor",
          "to": "ListProfileThreadsRequest.max_id",
          "description": "分页游标来自上一次真实响应。"
        }
      ],
      "missing_capabilities": []
    }
  ],
  "services": [
    {
      "name": "threads.auth.v1.AuthService",
      "rpcs": [
        {
          "name": "Login",
          "identifier": "threads.auth.v1.AuthService/Login",
          "request_type": "LoginRequest",
          "response_type": "LoginResponse",
          "proto": "/proto/threads/auth.proto",
          "status": "experimental",
          "operation": "create",
          "idempotent": false,
          "side_effect": true,
          "python_method": "AuthClient.login",
          "business_success": "success=true、account_state.session.token 为 IGT:2，且 verified_user.username 与请求账号一致；stable_for_automation 仍必须为 false，直到硬件证明和长期存活观测另行通过。",
          "notes": "account_state 是后续独立任务的完整输入，SDK 与 Go server 均不负责持久化。Go server 从成套真机模板生成首次登录设备，并使用生产镜像内固定 keybox signer；keybox 不得由 SDK 用户传递或从 Go server 镜像移除，无 signer 时返回 FAILED_PRECONDITION。代理可省略；登录只接受 Python Proxy 强类型对象，Go server 契约同样使用 Proxy 消息。协议支持 HTTP(S)、SOCKS5、SOCKS5H，认证账号密码必须成对提供或同时省略，省略时支持 IP 白名单代理。配置字符串只能通过 Proxy.from_url() 显式转换，不存在 proxy_url 兼容入口。登录成功后必须保存并复用 account_state。",
          "title": "账号登录",
          "module": "auth",
          "use_when": "没有可用 IGT:2，并且需要通过账密建立新会话时。",
          "prerequisites": [
            "生产容器已固定加载 keybox signer；账号代理可选。"
          ],
          "parameter_sources": {
            "username": "用户输入：Threads/Instagram 登录账号。",
            "password": "受保护账号配置：禁止写入源码和日志。",
            "two_factor_seed": "受保护账号配置：启用 TOTP 时提供。",
            "proxy": "可选账号出口：由 Proxy 强类型对象提供；认证账号密码必须同时提供或同时省略。"
          },
          "response_usage": "保存 account_state，核对 verified_user.username 和 assurance；后续任务用 ThreadsClient.account(state) 恢复，stable_for_automation 不能由即时成功推断。",
          "next_calls": [
            "threads.posts.v1.PostsService/CreateTextPost"
          ],
          "workflows": [
            "login"
          ]
        },
        {
          "name": "GetCurrentUser",
          "identifier": "threads.auth.v1.AuthService/GetCurrentUser",
          "request_type": "GetCurrentUserRequest",
          "response_type": "GetCurrentUserResponse",
          "proto": "/proto/threads/auth.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "AccountAuthClient.get_current_user",
          "business_success": "响应 pk 与 AccountState.uid/session 所属账号一致，且 AccountClient.state 已由内部响应信封刷新。",
          "notes": "读取当前账号；edit=true 时返回更完整的资料字段。必须从 AccountClient 调用。",
          "title": "读取当前账号",
          "module": "auth",
          "use_when": "校验 AccountState 会话、取得当前 uid，或在资料编辑前读取完整原值时。",
          "prerequisites": [
            "已通过 client.account(state) 恢复完整账号状态。"
          ],
          "parameter_sources": {
            "edit": "调用场景：普通身份校验传 false；资料编辑前传 true。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "pk 是发帖 uid 和当前账号 user_id；edit=true 时使用完整资料字段回填 EditProfile。",
          "next_calls": [
            "threads.profile.v1.ProfileService/EditProfile",
            "threads.posts.v1.PostsService/CreateTextPost",
            "threads.posts.v1.PostsService/CreateImagePost"
          ],
          "workflows": [
            "verify_session",
            "login",
            "edit_profile",
            "create_text_post",
            "create_image_post"
          ]
        }
      ]
    },
    {
      "name": "threads.feed.v1.FeedService",
      "rpcs": [
        {
          "name": "ListRecommendedUsers",
          "identifier": "threads.feed.v1.FeedService/ListRecommendedUsers",
          "request_type": "ListRecommendedUsersRequest",
          "response_type": "threads.common.v1.ListPageResponse",
          "proto": "/proto/threads/feed.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "FeedClient.list_recommended_users",
          "business_success": "ListPage.items 与请求推荐场景一致，paging 游标来自同一次真实响应。",
          "notes": "返回结构化 items/paging/metadata，不暴露原始 JSON 字符串。",
          "title": "读取推荐用户",
          "module": "feed",
          "use_when": "需要获取 Threads 推荐账号或继续推荐流分页时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "paging_token": "上一页响应：使用 ListPage.paging.next_cursor；首次调用省略。",
            "recommendation_type": "产品场景配置：按上游支持的推荐类型填写，未知时省略。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "直接遍历 items；paging.next_cursor 用于下一页 paging_token；metadata 保留未知顶层字段。",
          "next_calls": [
            "threads.feed.v1.FeedService/ListRecommendedUsers"
          ],
          "workflows": []
        }
      ]
    },
    {
      "name": "threads.friendships.v1.FriendshipsService",
      "rpcs": [
        {
          "name": "GetFriendshipStatus",
          "identifier": "threads.friendships.v1.FriendshipsService/GetFriendshipStatus",
          "request_type": "GetFriendshipStatusRequest",
          "response_type": "GetFriendshipStatusResponse",
          "proto": "/proto/threads/friendships.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "FriendshipsClient.get_friendship_status",
          "business_success": "返回关系状态属于请求 user_id 对应的目标账号。",
          "notes": "读取关注、拉黑、静音等关系状态。",
          "title": "读取关系状态",
          "module": "friendships",
          "use_when": "需要确认当前账号是否关注、被关注、拉黑或静音目标账号时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "user_id": "目标对象：来自 GetUserInfo.pk、搜索结果或业务数据库。",
            "is_external_deeplink_profile_view": "调用场景：普通 API 调用使用 false。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "直接读取 following、followed_by、blocking、muting 等强类型字段。",
          "next_calls": [],
          "workflows": []
        },
        {
          "name": "FollowUser",
          "identifier": "threads.friendships.v1.FriendshipsService/FollowUser",
          "request_type": "FollowUserRequest",
          "response_type": "FollowUserResponse",
          "proto": "/proto/threads/friendships.proto",
          "status": "available",
          "operation": "create",
          "idempotent": true,
          "side_effect": true,
          "python_method": "FriendshipsClient.follow_user",
          "business_success": "result.status.following=true，并用 GetFriendshipStatus 独立回读确认关注生效。",
          "notes": "真实关系写操作；previous_following=false 表示本次确实产生变化，true 表示此前已关注。",
          "title": "关注用户",
          "module": "friendships",
          "use_when": "关注目标账号时。",
          "prerequisites": [
            "完整 AccountState、目标 user_id、写操作授权。"
          ],
          "parameter_sources": {
            "user_id": "用户输入或前置响应：目标用户数字 id，可来自 GetUserInfo、推荐流或搜索结果。",
            "container_module": "固定参数：缺省 ig_text_feed_profile；从帖子详情页操作时传 ig_text_post_permalink。",
            "nav_chain": "埋点参数：可留空；需要贴合真机时用进入该用户页那次调用的导航链。",
            "attribution_media_id": "前置响应：从某个帖子发起关注时传该帖复合 media_id（{pk}_{author_uid}）；直接按 user_id 操作时留空。",
            "ranking_info_token": "前置响应：时间线/回复流里该帖的排序归因 token；留空则不发送。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "以 result.status.following 判定结果；previous_following 区分「本次新增关注」与「此前已关注」。",
          "next_calls": [
            "threads.friendships.v1.FriendshipsService/GetFriendshipStatus"
          ],
          "workflows": []
        },
        {
          "name": "UnfollowUser",
          "identifier": "threads.friendships.v1.FriendshipsService/UnfollowUser",
          "request_type": "UnfollowUserRequest",
          "response_type": "UnfollowUserResponse",
          "proto": "/proto/threads/friendships.proto",
          "status": "available",
          "operation": "delete",
          "idempotent": true,
          "side_effect": true,
          "python_method": "FriendshipsClient.unfollow_user",
          "business_success": "result.status.following=false，并用 GetFriendshipStatus 独立回读确认已取关。",
          "notes": "真实关系写操作；取关不返回 previous_following，不要依赖该字段判断结果。",
          "title": "取消关注",
          "module": "friendships",
          "use_when": "取消对目标账号的关注时。",
          "prerequisites": [
            "完整 AccountState、目标 user_id、写操作授权。"
          ],
          "parameter_sources": {
            "user_id": "用户输入或前置响应：目标用户数字 id，可来自 GetUserInfo、推荐流或搜索结果。",
            "container_module": "固定参数：缺省 ig_text_feed_profile；从帖子详情页操作时传 ig_text_post_permalink。",
            "nav_chain": "埋点参数：可留空；需要贴合真机时用进入该用户页那次调用的导航链。",
            "attribution_media_id": "前置响应：从某个帖子发起关注时传该帖复合 media_id（{pk}_{author_uid}）；直接按 user_id 操作时留空。",
            "ranking_info_token": "前置响应：时间线/回复流里该帖的排序归因 token；留空则不发送。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "以 result.status.following=false 判定结果；该接口不返回 previous_following。",
          "next_calls": [
            "threads.friendships.v1.FriendshipsService/GetFriendshipStatus"
          ],
          "workflows": []
        }
      ]
    },
    {
      "name": "threads.posts.v1.PostsService",
      "rpcs": [
        {
          "name": "CheckOffensiveText",
          "identifier": "threads.posts.v1.PostsService/CheckOffensiveText",
          "request_type": "CheckOffensiveTextRequest",
          "response_type": "CheckOffensiveTextResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "known-upstream-error",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "PostsClient.check_offensive_text",
          "business_success": "当前真实上游返回 HTTP 404，测试以真实错误映射为验收结果。",
          "notes": "请求结构与真实 App 抓包一致，但当前线上端点不可用。",
          "title": "检查冒犯文本",
          "module": "posts",
          "use_when": "发帖前希望执行上游文本风险检查时；当前真实上游返回 404。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "text_list": "用户内容：待检查的一段或多段文本。",
            "media_id": "已有媒体上下文：编辑或关联媒体时提供，否则省略。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "当前以 gRPC 错误路径处理，不能把未返回结果视为安全。",
          "next_calls": [],
          "workflows": []
        },
        {
          "name": "CreateTextPost",
          "identifier": "threads.posts.v1.PostsService/CreateTextPost",
          "request_type": "CreateTextPostRequest",
          "response_type": "CreateTextPostResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "create",
          "idempotent": false,
          "side_effect": true,
          "python_method": "PostsClient.create_text_post",
          "business_success": "返回帖子属于 account_state.uid，caption 与请求一致，并能从账号主页重新读取；AccountClient.state 已刷新。",
          "notes": "推荐通过 AccountClient 调用并在任务完成后保存 AccountClient.state。真实发帖操作；必须显式授权，调用方不得自动重试。",
          "title": "创建文本帖",
          "module": "posts",
          "use_when": "发布不带图片或视频的 Threads 文本帖子时；传 reply_id 时同一接口用于发布回复（评论）。",
          "prerequisites": [
            "完整 AccountState、写操作授权。"
          ],
          "parameter_sources": {
            "caption": "用户输入：帖子正文。",
            "upload_id": "运行时生成：可省略，由 Go SDK 生成；重试时不得随意复用。",
            "reply_control": "产品设置：回复权限枚举，默认 0。",
            "camera_session_id": "运行时生成：模拟发布会话时提供，否则省略。",
            "nav_chain": "运行时上下文：导航链，普通调用可省略。",
            "timezone_offset": "运行环境：账号所在地时区偏移。",
            "tag_header": "主题功能：先用 ValidateTag 校验后按上游格式提供。",
            "location": "用户选择：posts_pb2.Location，未选择位置时省略。",
            "poll": "用户输入：posts_pb2.Poll，未创建投票时省略。",
            "reply_id": "可选参数：非空即表示发布回复（评论），取被回复帖的纯数字 pk（Media.pk 或 post.pk，不是 {pk}_{author_uid} 复合 id）；留空为新建独立帖。",
            "ranking_info_token": "前置响应：时间线/回复流响应里该帖的排序归因 token；留空则不发送该字段。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态，包含 uid、设备、Session、可选代理和 App 版本。"
          },
          "response_usage": "保存 id/code/permalink，核对 caption、user 和主页结果；SDK 从内部响应信封刷新状态，调用方将 AccountClient.state 覆盖写回自己的存储。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": [
            "create_text_post"
          ]
        },
        {
          "name": "UploadImage",
          "identifier": "threads.posts.v1.PostsService/UploadImage",
          "request_type": "UploadImageRequest",
          "response_type": "UploadImageResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "create",
          "idempotent": false,
          "side_effect": true,
          "python_method": "PostsClient.upload_image",
          "business_success": "响应 status=ok 且 upload_id 非空；该 upload_id 可被 CreateImagePost 接受。",
          "notes": "真实图片二进制上传；当前抓包确认 image/webp，视频上传仍缺少抓包来源。",
          "title": "上传图片",
          "module": "posts",
          "use_when": "创建单图帖前上传 WebP 图片二进制时。",
          "prerequisites": [
            "完整 AccountState、WebP 图片字节、真实宽高、写操作授权。"
          ],
          "parameter_sources": {
            "image_data": "本地文件或内存：WebP 原始 bytes。",
            "original_width": "图片元数据：解码后的真实像素宽度。",
            "original_height": "图片元数据：解码后的真实像素高度。",
            "upload_id": "运行时生成：通常省略并由 Go SDK 生成。",
            "mime_type": "固定协议值：当前确认 image/webp。",
            "is_optimistic_upload": "上传策略：通常省略使用 SDK 默认值。",
            "msssim": "图片质量计算：调用方实际计算时提供，否则省略。",
            "ssim": "图片质量计算：调用方实际计算时提供，否则省略。",
            "waterfall_id": "运行时生成：上传链路追踪 ID，通常省略。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "status 必须为 ok；将 upload_id 原样传给 CreateImagePost。",
          "next_calls": [
            "threads.posts.v1.PostsService/CreateImagePost"
          ],
          "workflows": [
            "create_image_post"
          ]
        },
        {
          "name": "CreateImagePost",
          "identifier": "threads.posts.v1.PostsService/CreateImagePost",
          "request_type": "CreateImagePostRequest",
          "response_type": "CreateImagePostResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "create",
          "idempotent": false,
          "side_effect": true,
          "python_method": "PostsClient.create_image_post",
          "business_success": "返回 media_type=1，正文与请求一致，image_versions2 非空，并能从账号主页重新读取。",
          "notes": "单图帖 configure；调用前必须先用 UploadImage 获取 upload_id，调用方不得自动重试。",
          "title": "创建单图帖",
          "module": "posts",
          "use_when": "UploadImage 成功后，把已上传图片发布为单图帖子时。",
          "prerequisites": [
            "UploadImageResult.status=ok、完整 AccountState、写操作授权。"
          ],
          "parameter_sources": {
            "caption": "用户输入：帖子正文。",
            "upload_id": "前置响应：UploadImageResult.upload_id。",
            "reply_control": "产品设置：回复权限枚举，默认 0。",
            "camera_session_id": "运行时生成：模拟发布会话时提供，否则省略。",
            "nav_chain": "运行时上下文：导航链，普通调用可省略。",
            "timezone_offset": "运行环境：账号所在地时区偏移。",
            "tag_header": "主题功能：先用 ValidateTag 校验后按上游格式提供。",
            "location": "用户选择：posts_pb2.Location，未选择位置时省略。",
            "poll": "用户输入：posts_pb2.Poll，未创建投票时省略。",
            "original_width": "前置请求：与 UploadImage.original_width 保持一致。",
            "original_height": "前置请求：与 UploadImage.original_height 保持一致。",
            "custom_accessibility_caption": "用户输入：图片无障碍说明，可省略。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "核对 media_type=1、image_versions2、caption 和 user，并保存 id/code/permalink。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": [
            "create_image_post"
          ]
        },
        {
          "name": "DeletePost",
          "identifier": "threads.posts.v1.PostsService/DeletePost",
          "request_type": "DeletePostRequest",
          "response_type": "DeletePostResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "delete",
          "idempotent": false,
          "side_effect": true,
          "python_method": "PostsClient.delete_post",
          "business_success": "did_delete=true，且重新读取主页确认目标帖子已经消失。",
          "notes": "真实删除操作；必须显式授权，调用方不得自动重试。",
          "title": "删除帖子",
          "module": "posts",
          "use_when": "删除当前账号已有帖子或清理测试帖子时。",
          "prerequisites": [
            "完整 AccountState、目标 media_id、写操作授权。"
          ],
          "parameter_sources": {
            "media_id": "前置响应：创建接口返回的 Media.id，或主页帖子中的 post.id。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "did_delete 必须为 true，并通过 ListProfileThreads 确认目标帖子消失。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": []
        },
        {
          "name": "LikeMedia",
          "identifier": "threads.posts.v1.PostsService/LikeMedia",
          "request_type": "LikeMediaRequest",
          "response_type": "LikeMediaResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "create",
          "idempotent": true,
          "side_effect": true,
          "python_method": "PostsClient.like_media",
          "business_success": "调用成功后重新读取该帖，has_liked 必须为 true。",
          "notes": "真实互动写操作；media_id 必须是 {pk}_{author_uid} 复合 id。上游成功响应只有 status，方法无返回值。",
          "title": "点赞帖子",
          "module": "posts",
          "use_when": "对指定帖子点赞时。",
          "prerequisites": [
            "完整 AccountState、目标帖子的复合 media_id、写操作授权。"
          ],
          "parameter_sources": {
            "media_id": "前置响应：创建接口返回的 Media.id，或主页帖子/时间线中的 post.id（形如 {pk}_{author_uid}）。",
            "container_module": "固定参数：缺省 ig_text_feed_timeline，从其他入口点赞时按实际来源传入。",
            "nav_chain": "埋点参数：可留空；需要贴合真机时用读取该帖那次调用的导航链。",
            "feed_position": "埋点参数：该帖在列表中的下标，缺省 0。",
            "logging_info_token": "前置响应：时间线响应里该帖的 logging token；留空则不发送。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "无业务返回值；成功判据是重新读取该帖，has_liked 为 true。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": []
        },
        {
          "name": "UnlikeMedia",
          "identifier": "threads.posts.v1.PostsService/UnlikeMedia",
          "request_type": "UnlikeMediaRequest",
          "response_type": "UnlikeMediaResponse",
          "proto": "/proto/threads/posts.proto",
          "status": "available",
          "operation": "delete",
          "idempotent": true,
          "side_effect": true,
          "python_method": "PostsClient.unlike_media",
          "business_success": "调用成功后重新读取该帖，has_liked 必须回到 false。",
          "notes": "TODO(抓包核对): 路径与字段集由 like 对称推导，尚未真机核对；失败时不要自动重试。",
          "title": "取消点赞",
          "module": "posts",
          "use_when": "撤销此前对某帖的点赞时。",
          "prerequisites": [
            "完整 AccountState、目标帖子的复合 media_id、写操作授权。"
          ],
          "parameter_sources": {
            "media_id": "前置响应：与 LikeMedia 使用同一个复合 media_id。",
            "container_module": "固定参数：缺省 ig_text_feed_timeline。",
            "nav_chain": "埋点参数：可留空。",
            "feed_position": "埋点参数：缺省 0。",
            "logging_info_token": "前置响应：时间线响应里该帖的 logging token；留空则不发送。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "无业务返回值；成功判据是重新读取该帖，has_liked 回到 false。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": []
        }
      ]
    },
    {
      "name": "threads.profile.v1.ProfileService",
      "rpcs": [
        {
          "name": "GetUserInfo",
          "identifier": "threads.profile.v1.ProfileService/GetUserInfo",
          "request_type": "GetUserInfoRequest",
          "response_type": "GetUserInfoResponse",
          "proto": "/proto/threads/profile.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "ProfileClient.get_user_info",
          "business_success": "响应 pk 与请求 user_id 一致。",
          "notes": "读取指定用户资料。",
          "title": "读取指定用户资料",
          "module": "profile",
          "use_when": "已知用户 ID，需要读取公开资料和账号统计时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "user_id": "目标对象：来自搜索结果、帖子作者 pk 或业务数据库。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "核对 pk 等于请求 user_id，再使用 username、计数和隐私状态。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads",
            "threads.friendships.v1.FriendshipsService/GetFriendshipStatus"
          ],
          "workflows": []
        },
        {
          "name": "ListProfileThreads",
          "identifier": "threads.profile.v1.ProfileService/ListProfileThreads",
          "request_type": "ListProfileThreadsRequest",
          "response_type": "ListProfileThreadsResponse",
          "proto": "/proto/threads/profile.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "ProfileClient.list_profile_threads",
          "business_success": "status=ok，threads[].items[].post 可直接读取，帖子作者与请求 user_id 的业务语义一致。",
          "notes": "主页帖子已强类型化；raw_json 仅为旧客户端线级兼容和未建模字段排错保留。",
          "title": "分页读取主页帖子",
          "module": "profile",
          "use_when": "采集指定账号主页帖子、验证发帖或验证删除结果时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState，并已准备目标 user_id。"
          ],
          "parameter_sources": {
            "user_id": "目标对象：当前账号用 GetCurrentUser.pk，其他账号来自搜索或资料接口。",
            "max_id": "上一页响应：使用 ProfileThreadsPage.next_cursor；首次调用省略。",
            "exclude_reposts": "采集策略：是否排除转发，可省略使用上游默认。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "优先消费 threads[].items[].post；next_cursor 传给下一页 max_id；raw_json 仅用于兼容排错。",
          "next_calls": [
            "threads.profile.v1.ProfileService/ListProfileThreads"
          ],
          "workflows": [
            "create_text_post",
            "create_image_post",
            "collect_profile_threads"
          ]
        },
        {
          "name": "EditProfile",
          "identifier": "threads.profile.v1.ProfileService/EditProfile",
          "request_type": "EditProfileRequest",
          "response_type": "EditProfileResponse",
          "proto": "/proto/threads/profile.proto",
          "status": "available",
          "operation": "update",
          "idempotent": false,
          "side_effect": true,
          "python_method": "ProfileClient.edit_profile",
          "business_success": "保存后重新读取当前账号，资料字段与请求值一致。",
          "notes": "整表回传接口；公开参数支持 username/first_name/biography/is_private/external_url/url_title，设备 UUID 只从 AccountState 内部取得。调用前应读取当前值；头像和封面属于尚未抓包的独立上传端点，location 不是该端点字段。",
          "title": "保存当前账号资料",
          "module": "profile",
          "use_when": "修改用户名、显示名、简介、隐私状态或外部链接时。",
          "prerequisites": [
            "先调用 GetCurrentUser(edit=true) 读取完整原值。"
          ],
          "parameter_sources": {
            "username": "前置响应或用户修改：未修改时回填 CurrentUser.username。",
            "first_name": "前置响应或用户修改：未修改时回填 CurrentUser.full_name。",
            "biography": "前置响应或用户修改：未修改时回填 CurrentUser.biography。",
            "is_private": "前置响应或用户修改：未修改时回填 CurrentUser.is_private。",
            "external_url": "前置响应或用户修改：未修改时回填 CurrentUser.external_url。",
            "url_title": "前置响应或用户修改：从 CurrentUser.bio_links 对应链接标题回填。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "核对返回资料，并再次 GetCurrentUser(edit=true) 验证真实保存结果。",
          "next_calls": [
            "threads.auth.v1.AuthService/GetCurrentUser"
          ],
          "workflows": [
            "edit_profile"
          ]
        }
      ]
    },
    {
      "name": "threads.replies.v1.RepliesService",
      "rpcs": [
        {
          "name": "ListProfileReplies",
          "identifier": "threads.replies.v1.RepliesService/ListProfileReplies",
          "request_type": "ListProfileRepliesRequest",
          "response_type": "threads.common.v1.ListPageResponse",
          "proto": "/proto/threads/replies.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "RepliesClient.list_profile_replies",
          "business_success": "ListPage.items 属于请求 user_id 的回复列表。",
          "notes": "返回结构化 items/paging/metadata，不暴露原始 JSON 字符串。",
          "title": "分页读取账号回复",
          "module": "replies",
          "use_when": "采集指定账号发布的回复时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState，并已准备目标 user_id。"
          ],
          "parameter_sources": {
            "user_id": "目标对象：来自当前账号 pk、搜索结果或资料接口。",
            "max_id": "上一页响应：使用 ListPage.paging.next_cursor；首次调用省略。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "直接遍历 items；paging.next_cursor 用于下一页 max_id。",
          "next_calls": [
            "threads.replies.v1.RepliesService/ListProfileReplies"
          ],
          "workflows": []
        }
      ]
    },
    {
      "name": "threads.search.v1.SearchService",
      "rpcs": [
        {
          "name": "KeywordSearch",
          "identifier": "threads.search.v1.SearchService/KeywordSearch",
          "request_type": "KeywordSearchRequest",
          "response_type": "threads.common.v1.ListPageResponse",
          "proto": "/proto/threads/search.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "SearchClient.keyword_search",
          "business_success": "ListPage.items 与请求 query 相关。",
          "notes": "返回结构化 items/paging/metadata，不暴露原始 JSON 字符串。",
          "title": "关键词搜索",
          "module": "search",
          "use_when": "按关键词查找 Threads 账号或内容时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "query": "用户输入：搜索关键词。",
            "page_token": "上一页 ListPage.paging.next_cursor；首次调用省略。",
            "rank_token": "首次响应或搜索会话上下文：后续页保持同一值。",
            "search_session_id": "运行时生成：同一轮搜索分页复用同一个会话 ID。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "直接遍历 items；metadata 保留未知字段；分页参数必须来自同一轮搜索响应和会话。",
          "next_calls": [
            "threads.search.v1.SearchService/KeywordSearch"
          ],
          "workflows": []
        },
        {
          "name": "ValidateTag",
          "identifier": "threads.search.v1.SearchService/ValidateTag",
          "request_type": "ValidateTagRequest",
          "response_type": "ValidateTagResponse",
          "proto": "/proto/threads/search.proto",
          "status": "available",
          "operation": "read",
          "idempotent": true,
          "side_effect": false,
          "python_method": "SearchClient.validate_tag",
          "business_success": "返回 is_valid/is_sensitive 对应请求 tag_name。",
          "notes": "发帖主题的前置校验。",
          "title": "校验发帖主题",
          "module": "search",
          "use_when": "发帖前需要确认主题标签是否有效或敏感时。",
          "prerequisites": [
            "AccountClient 已绑定完整 AccountState。"
          ],
          "parameter_sources": {
            "tag_name": "用户输入：不含 # 的主题名称。",
            "account_state": "SDK 自动注入：来自 AccountClient 当前完整状态。"
          },
          "response_usage": "is_valid=true 且业务允许时，才构造发帖 tag_header；is_sensitive 需触发产品确认。",
          "next_calls": [
            "threads.posts.v1.PostsService/CreateTextPost",
            "threads.posts.v1.PostsService/CreateImagePost"
          ],
          "workflows": []
        }
      ]
    }
  ]
}
