{
    "info": {
        "title": "HAF FYP - For You Page",
        "license": {
            "url": "https://opensource.org/license/mit",
            "name": "MIT License"
        },
        "version": "3.0.0",
        "description": "Personalized content feed API for the Hive blockchain. Provides algorithmically ranked post feeds, user profiles, and algorithm version management."
    },
    "tags": [
        {
            "name": "Feed",
            "description": "Personalized and global content feeds"
        },
        {
            "name": "Algorithm",
            "description": "Algorithm version registry"
        },
        {
            "name": "User",
            "description": "User profile information"
        },
        {
            "name": "Other",
            "description": "Health and version endpoints"
        }
    ],
    "paths": {
        "/health": {
            "get": {
                "tags": [
                    "Other"
                ],
                "summary": "Health check",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "status": {
                                                "type": "string",
                                                "example": "ok"
                                            },
                                            "version": {
                                                "type": "string"
                                            },
                                            "last_processed_block": {
                                                "type": "integer"
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "TABLE(status text, last_processed_block int, version text)"
                                }
                            }
                        },
                        "description": "Health status"
                    }
                },
                "description": "Returns service health and last processed block number.",
                "operationId": "haf_fyp_endpoints.get_health"
            }
        },
        "/version": {
            "get": {
                "tags": [
                    "Other"
                ],
                "summary": "Application version",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        },
                        "description": "Git hash string"
                    }
                },
                "description": "Returns the git hash of the currently deployed version.",
                "operationId": "haf_fyp_endpoints.get_version"
            }
        },
        "/sync-status": {
            "get": {
                "tags": [
                    "Other"
                ],
                "summary": "Sync status",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "required": [
                                        "last_block_num",
                                        "last_block_time"
                                    ],
                                    "properties": {
                                        "history": {
                                            "enum": [
                                                "waiting_for_hivemind",
                                                "following_hivemind",
                                                "unknown"
                                            ],
                                            "type": "string",
                                            "description": "Whether haf_fyp has copied its history window from hivemind yet: waiting_for_hivemind until the backfill has run, then following_hivemind"
                                        },
                                        "last_block_num": {
                                            "type": "integer",
                                            "description": "last block consumed by the haf_fyp HAF context"
                                        },
                                        "last_block_time": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "format": "date-time",
                                            "description": "UTC timestamp of that block; null if it has no hafd.blocks row"
                                        }
                                    },
                                    "x-sql-datatype": "JSON"
                                },
                                "example": {
                                    "history": "following_hivemind",
                                    "last_block_num": 109603308,
                                    "last_block_time": "2026-09-03T22:46:54"
                                }
                            }
                        },
                        "description": "last_block_time is null when that block has no hafd.blocks row - i.e. nothing has been processed yet (there is no block 0), or the node is pruned and the context is behind the prune tail."
                    },
                    "500": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "code": {
                                            "type": "string",
                                            "description": "PostgreSQL SQLSTATE, '55000' for both cases"
                                        },
                                        "hint": {
                                            "type": [
                                                "string",
                                                "null"
                                            ]
                                        },
                                        "details": {
                                            "type": [
                                                "string",
                                                "null"
                                            ]
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "code": "55000",
                                    "hint": null,
                                    "details": null,
                                    "message": "HAF instance is not ready (massive sync in progress)"
                                }
                            }
                        },
                        "description": "Unavailable, and the two cases are distinguished by message rather than by status. Either the HAF instance is still in massive sync (hafd.blocks' primary key is dropped for the duration, so the call fails fast instead of seq-scanning the largest table in the database), or the HAF context is not registered on this node. Both are raised as SQLSTATE 55000 -- the code every other HAF app uses for the massive-sync guard -- which PostgREST maps to 500. 503 would be the better status for a temporary condition, but it is not what the ecosystem does, and one app diverging is worse than the imperfection; changing it belongs everywhere at once."
                    }
                },
                "description": "Last block consumed by the haf_fyp HAF context, and its timestamp. The uniform HAF-app freshness endpoint: same two keys and the same timestamp format as haf_stats' /sync-status. NOTE this reports REACHABILITY and the context head, not liveness -- a stalled app whose context sits at block 0 answers 200 with a null timestamp, which is a state observed in the wild (tests/ci_invariants.sql). Pair it with a block-freshness agent-check, as haf_api_node does for its own apps.",
                "operationId": "haf_fyp_endpoints.get_sync_status"
            }
        },
        "/v1/fyp/global": {
            "get": {
                "tags": [
                    "Feed"
                ],
                "summary": "Global feed",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "$ref": "#/components/schemas/feed_entry"
                                    },
                                    "x-sql-datatype": "jsonb"
                                }
                            }
                        },
                        "description": "Global ranked post list"
                    }
                },
                "parameters": [
                    {
                        "in": "query",
                        "name": "page",
                        "schema": {
                            "type": "integer",
                            "default": 1
                        },
                        "required": false,
                        "description": "1-based page number"
                    },
                    {
                        "in": "query",
                        "name": "page-size",
                        "schema": {
                            "type": "integer",
                            "default": 20
                        },
                        "required": false,
                        "description": "Posts per page (max 20)"
                    },
                    {
                        "in": "query",
                        "name": "community_id",
                        "schema": {
                            "type": [
                                "integer",
                                "null"
                            ],
                            "default": null
                        },
                        "required": false,
                        "description": "Filter results to a specific community (optional)"
                    },
                    {
                        "in": "query",
                        "name": "truncate_body",
                        "schema": {
                            "type": "integer",
                            "default": 0
                        },
                        "required": false,
                        "description": "Truncate each post body to this many characters (0 = full body)"
                    }
                ],
                "description": "Returns the global unauthenticated feed, optionally filtered by community. Each post is returned in the same shape as Hivemind's bridge.get_ranked_posts, plus a nested \"fyp\" object with this app's ranking data, so the feed is a drop-in alternative content source for front ends.",
                "operationId": "haf_fyp_endpoints.get_global_feed"
            }
        },
        "/v1/fyp/algorithm": {
            "get": {
                "tags": [
                    "Algorithm"
                ],
                "summary": "Active algorithm",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "version": {
                                                "type": "string",
                                                "example": "1.0.0"
                                            },
                                            "weights": {
                                                "type": "object",
                                                "example": {
                                                    "recency": 0.3,
                                                    "relevance": 0.4,
                                                    "engagement": 0.2,
                                                    "credibility": 0.1,
                                                    "community_boost": 0.2
                                                },
                                                "additionalProperties": true
                                            },
                                            "deployed_at": {
                                                "type": "string",
                                                "format": "date-time"
                                            },
                                            "description": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "TABLE(version text, description text, weights jsonb, deployed_at timestamp)"
                                }
                            }
                        },
                        "description": "Active algorithm version"
                    }
                },
                "description": "Returns the currently active scoring algorithm version and its weights.",
                "operationId": "haf_fyp_endpoints.get_algorithm"
            }
        },
        "/v1/fyp/algorithms": {
            "get": {
                "tags": [
                    "Algorithm"
                ],
                "summary": "All algorithm versions",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "version": {
                                                "type": "string"
                                            },
                                            "weights": {
                                                "type": "object",
                                                "additionalProperties": true
                                            },
                                            "is_active": {
                                                "type": "boolean"
                                            },
                                            "deployed_at": {
                                                "type": "string",
                                                "format": "date-time"
                                            },
                                            "description": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "TABLE(version text, description text, weights jsonb, deployed_at timestamp, is_active boolean)"
                                }
                            }
                        },
                        "description": "List of all algorithm versions"
                    }
                },
                "description": "Lists all algorithm versions in the registry with their weights and active status.",
                "operationId": "haf_fyp_endpoints.list_algorithm_versions"
            }
        },
        "/v1/fyp/feed-metrics": {
            "get": {
                "tags": [
                    "Feed"
                ],
                "summary": "Feed quality metrics",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "rank": {
                                                "type": "integer"
                                            },
                                            "opens": {
                                                "type": "integer"
                                            },
                                            "source": {
                                                "type": "string"
                                            },
                                            "videos": {
                                                "type": "integer"
                                            },
                                            "actions": {
                                                "type": "integer"
                                            },
                                            "open_rate": {
                                                "type": [
                                                    "number",
                                                    "null"
                                                ],
                                                "description": "opens / impressions (closest to a click-through)"
                                            },
                                            "impressions": {
                                                "type": "integer"
                                            },
                                            "avg_dwell_ms": {
                                                "type": [
                                                    "number",
                                                    "null"
                                                ]
                                            },
                                            "avg_watch_ms": {
                                                "type": [
                                                    "number",
                                                    "null"
                                                ]
                                            },
                                            "engagement_rate": {
                                                "type": [
                                                    "number",
                                                    "null"
                                                ],
                                                "description": "actions (vote+comment+reblog+follow) / impressions"
                                            },
                                            "algorithm_version": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "jsonb"
                                }
                            }
                        },
                        "description": "Aggregated metric rows"
                    }
                },
                "parameters": [
                    {
                        "in": "query",
                        "name": "from_date",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "format": "date",
                            "default": null,
                            "x-sql-datatype": "DATE"
                        },
                        "required": false,
                        "description": "Inclusive start date (YYYY-MM-DD). Default 30 days ago."
                    },
                    {
                        "in": "query",
                        "name": "to_date",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "format": "date",
                            "default": null,
                            "x-sql-datatype": "DATE"
                        },
                        "required": false,
                        "description": "Inclusive end date (YYYY-MM-DD). Default today."
                    },
                    {
                        "in": "query",
                        "name": "algorithm_version",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "default": null
                        },
                        "required": false,
                        "description": "Filter to one algorithm version (optional)"
                    },
                    {
                        "in": "query",
                        "name": "source",
                        "schema": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "default": null
                        },
                        "required": false,
                        "description": "Filter to 'personalized' or 'global' (optional)"
                    }
                ],
                "description": "Aggregated feed-quality metrics from the telemetry rollup (feed_metrics_daily, #12), summed over a date range and grouped by algorithm_version / source / rank. Enables engagement-rate@rank, open rate, and average dwell/watch — and A/B comparison across algorithm_version. NON-AUTHORITATIVE: derived from the unauthenticated /v1/fyp/events log (username is client-claimed), so treat figures as directional and corroborate against on-chain engagement. Global-feed responses now carry the algorithm_version that produced them (#23), so clients that echo it back yield attributed global rows; the server never derives this column, so anything a client omits still lands in the '' bucket (feed_metrics_daily.algorithm_version is NOT NULL DEFAULT ''). Rows recorded before #23 sit in '' for the same reason and are not backfilled. The global feed is identical for every viewer, so global-source attribution supports longitudinal before/after comparison rather than concurrent A/B arms.",
                "operationId": "haf_fyp_endpoints.get_feed_metrics"
            }
        },
        "/v1/fyp/feed/{username}": {
            "get": {
                "tags": [
                    "Feed"
                ],
                "summary": "Personalized feed",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "$ref": "#/components/schemas/feed_entry"
                                    },
                                    "x-sql-datatype": "jsonb"
                                }
                            }
                        },
                        "description": "Ranked post list (empty array before first ranker cycle)"
                    }
                },
                "parameters": [
                    {
                        "in": "path",
                        "name": "username",
                        "schema": {
                            "type": "string"
                        },
                        "required": true,
                        "description": "Hive account name"
                    },
                    {
                        "in": "query",
                        "name": "page",
                        "schema": {
                            "type": "integer",
                            "default": 1
                        },
                        "required": false,
                        "description": "1-based page number"
                    },
                    {
                        "in": "query",
                        "name": "page-size",
                        "schema": {
                            "type": "integer",
                            "default": 20
                        },
                        "required": false,
                        "description": "Posts per page (max 20)"
                    },
                    {
                        "in": "query",
                        "name": "truncate_body",
                        "schema": {
                            "type": "integer",
                            "default": 0
                        },
                        "required": false,
                        "description": "Truncate each post body to this many characters (0 = full body)"
                    }
                ],
                "description": "Returns a ranked list of posts for the given user, ordered by final score. Each post is returned in the same shape as Hivemind's bridge.get_ranked_posts (the structure front ends already render for trending / hot / new), plus a nested \"fyp\" object carrying this app's ranking data, so the feed is a drop-in alternative content source. Falls back to the global feed until the ranker has cached a feed for the user. Pages of one read session are stable: the first page opens a session, and the ranker does not rewrite the feed until the reader has requested no page for its session window (15 minutes by default), so page N+1 continues page N without repeating or skipping a post. The feed is re-ranked after the session ends; a reader who stays away longer than that starts the next session on a fresh ranking. An account that reads the global fallback stays on it for the session.",
                "operationId": "haf_fyp_endpoints.get_personalized_feed"
            }
        },
        "/v1/fyp/warmup/{username}": {
            "get": {
                "tags": [
                    "Feed"
                ],
                "summary": "Ask for a fresh feed before it is read",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "x-sql-datatype": "void"
                                }
                            }
                        },
                        "description": "Never sent: the function returns nothing, which PostgREST answers with 204. Listed because the code generator takes the return type from it."
                    },
                    "204": {
                        "description": "Heard, or nothing to do. No body either way."
                    }
                },
                "parameters": [
                    {
                        "in": "path",
                        "name": "username",
                        "schema": {
                            "type": "string"
                        },
                        "required": true,
                        "description": "Hive account name"
                    }
                ],
                "description": "Asks the ranker to re-rank this account's feed now if it is older than the ranker's minimum age (10 minutes by default), so that the reader's next visit starts on a fresh ranking. Meant for a client to call when the user signs in or opens the app, before it requests the first page of the feed: reading the feed opens a read session, during which the feed is not rewritten, so a re-rank asked for by the read itself only happens after the session. A warm-up does not open a session. One that arrives while a session is open is carried out after the session ends. It is rationed like the feed read: at most one signal per account per ten minutes reaches the ranker, and the ranker's per-cycle ceilings apply. Answers 204 with no body whether or not anything was asked of the ranker; it never returns the feed.",
                "operationId": "haf_fyp_endpoints.warm_up_feed"
            }
        },
        "/v1/fyp/profile/{username}": {
            "get": {
                "tags": [
                    "User"
                ],
                "summary": "User profile",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "has_feed": {
                                                "type": "boolean",
                                                "description": "True if the user has a precomputed personalized feed (vs the global fallback)"
                                            },
                                            "has_vector": {
                                                "type": "boolean"
                                            },
                                            "updated_at": {
                                                "type": "string",
                                                "format": "date-time"
                                            },
                                            "posts_sampled": {
                                                "type": "integer"
                                            },
                                            "top_communities": {
                                                "type": "array",
                                                "items": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "TABLE(has_feed boolean, has_vector boolean, posts_sampled int, top_communities int[], updated_at timestamp)"
                                }
                            }
                        },
                        "description": "User profile data (always one row; has_feed/has_vector are false if the user has not been ranked yet)"
                    }
                },
                "parameters": [
                    {
                        "in": "path",
                        "name": "username",
                        "schema": {
                            "type": "string"
                        },
                        "required": true,
                        "description": "Hive account name"
                    }
                ],
                "description": "Returns a user's computed interest profile - whether a vector has been built and which communities are tracked.",
                "operationId": "haf_fyp_endpoints.get_user_profile"
            }
        },
        "/v1/fyp/explain/{username}/{post_id}": {
            "get": {
                "tags": [
                    "Feed"
                ],
                "summary": "Score explanation",
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "rank": {
                                                "type": "integer"
                                            },
                                            "title": {
                                                "type": "string"
                                            },
                                            "author": {
                                                "type": "string"
                                            },
                                            "post_id": {
                                                "type": "integer"
                                            },
                                            "permlink": {
                                                "type": "string"
                                            },
                                            "computed_at": {
                                                "type": "string",
                                                "format": "date-time"
                                            },
                                            "final_score": {
                                                "type": "number"
                                            },
                                            "boost_source": {
                                                "type": [
                                                    "string",
                                                    "null"
                                                ],
                                                "description": "Which tier applied the community boost - 'subscribed', 'engaged', or null"
                                            },
                                            "score_recency": {
                                                "type": "number"
                                            },
                                            "score_relevance": {
                                                "type": "number"
                                            },
                                            "score_engagement": {
                                                "type": "number"
                                            },
                                            "algorithm_version": {
                                                "type": "string"
                                            },
                                            "algorithm_weights": {
                                                "type": "object",
                                                "additionalProperties": true
                                            },
                                            "score_credibility": {
                                                "type": "number"
                                            },
                                            "community_boost_applied": {
                                                "type": "boolean"
                                            }
                                        }
                                    },
                                    "x-sql-datatype": "TABLE(post_id bigint, author varchar(16), permlink varchar(256), title text, final_score float, score_relevance float, score_recency float, score_engagement float, score_credibility float, community_boost_applied boolean, boost_source varchar(16), rank int, algorithm_version varchar(16), algorithm_weights jsonb, computed_at timestamp)"
                                }
                            }
                        },
                        "description": "Score breakdown (empty if post not in user's feed cache)"
                    }
                },
                "parameters": [
                    {
                        "in": "path",
                        "name": "username",
                        "schema": {
                            "type": "string"
                        },
                        "required": true,
                        "description": "Hive account name"
                    },
                    {
                        "in": "path",
                        "name": "post_id",
                        "schema": {
                            "type": "integer",
                            "x-sql-datatype": "BIGINT"
                        },
                        "required": true,
                        "description": "Internal post ID"
                    }
                ],
                "description": "Returns a per-signal score breakdown for a specific post in a user's feed cache.",
                "operationId": "haf_fyp_endpoints.get_score_explanation"
            }
        }
    },
    "openapi": "3.1.0",
    "servers": [
        {
            "url": "/haf-fyp-api"
        }
    ],
    "components": {
        "schemas": {
            "feed_entry": {
                "type": "object",
                "example": {
                    "fyp": {
                        "rank": 1,
                        "source": "personalized",
                        "final_score": 0.9178,
                        "boost_source": "subscribed",
                        "score_recency": 0.9926,
                        "score_relevance": 0.62,
                        "score_engagement": 0.9805,
                        "score_credibility": 0.5679,
                        "community_boost_applied": true
                    },
                    "url": "/hive-115814/@cryptoandcoffee/will-we-see-the-hydration-break-next-season",
                    "body": "<center><img src=\"https://images.hive.blog/p/cover.png\"/></center>\n\nMarkdown body of the post...",
                    "depth": 0,
                    "stats": {
                        "gray": false,
                        "hide": false,
                        "flag_weight": 0.0,
                        "total_votes": 127
                    },
                    "title": "Will We See The Hydration / Advertising Break In The Premier League Next Season?",
                    "author": "cryptoandcoffee",
                    "payout": 3.655,
                    "created": "2026-06-19T16:33:57",
                    "reblogs": 0,
                    "replies": [
                    ],
                    "updated": "2026-06-19T16:33:57",
                    "category": "hive-115814",
                    "children": 4,
                    "permlink": "will-we-see-the-hydration-break-next-season",
                    "community": "hive-115814",
                    "payout_at": "2026-06-26T16:33:57",
                    "blacklists": [
                    ],
                    "is_paidout": false,
                    "author_role": "guest",
                    "net_rshares": 50938299772643,
                    "percent_hbd": 10000,
                    "active_votes": [
                        {
                            "voter": "team",
                            "rshares": 30982500342
                        },
                        {
                            "voter": "roelandp",
                            "rshares": 15794227518
                        }
                    ],
                    "author_title": "",
                    "beneficiaries": [
                        {
                            "weight": 500,
                            "account": "leo.voter"
                        }
                    ],
                    "json_metadata": {
                        "app": "leothreads/0.3",
                        "tags": [
                            "sports",
                            "football",
                            "premierleague",
                            "advertising"
                        ],
                        "image": [
                            "https://images.hive.blog/p/cover.png"
                        ],
                        "format": "markdown"
                    },
                    "community_title": "Sportsblock",
                    "author_reputation": 84.5,
                    "author_payout_value": "0.000 HBD",
                    "max_accepted_payout": "1000000.000 HBD",
                    "curator_payout_value": "0.000 HBD",
                    "pending_payout_value": "3.655 HBD"
                },
                "properties": {
                    "fyp": {
                        "$ref": "#/components/schemas/fyp_ranking"
                    },
                    "url": {
                        "type": "string"
                    },
                    "body": {
                        "type": "string"
                    },
                    "stats": {
                        "type": "object"
                    },
                    "title": {
                        "type": "string"
                    },
                    "author": {
                        "type": "string"
                    },
                    "payout": {
                        "type": "number"
                    },
                    "created": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "category": {
                        "type": "string"
                    },
                    "children": {
                        "type": "integer"
                    },
                    "permlink": {
                        "type": "string"
                    },
                    "community": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "active_votes": {
                        "type": "array",
                        "items": {
                            "type": "object"
                        }
                    },
                    "json_metadata": {
                        "type": "object"
                    },
                    "author_reputation": {
                        "type": "number"
                    }
                },
                "description": "A ranked post. The post itself is returned in the exact shape produced by Hivemind's bridge.get_ranked_posts (author, permlink, title, body, json_metadata, category, created/updated, payout / pending_payout / is_paidout / payout_at, author_reputation, author_role/title, children, net_rshares, active_votes, stats, beneficiaries, percent_hbd, max_accepted_payout, community / community_title, url, blacklists, reblogs, ...), so any front end that renders trending / hot / new can render this feed unchanged. A nested \"fyp\" object adds this app's ranking data.",
                "x-sql-datatype": "object"
            },
            "fyp_ranking": {
                "type": "object",
                "properties": {
                    "rank": {
                        "type": "integer",
                        "description": "Position in the feed (1-based)"
                    },
                    "source": {
                        "enum": [
                            "personalized",
                            "global"
                        ],
                        "type": "string",
                        "description": "Which feed produced this entry - 'personalized' when the user has a precomputed personalized feed, 'global' when served the cold-start global fallback. Uniform across all entries in a single response."
                    },
                    "post_id": {
                        "type": "string",
                        "description": "Internal post id (joins feed_events.post_id). Serialized as a STRING because the id exceeds JavaScript's 2^53 safe-integer limit - a JS Number would lose precision and break the telemetry join; echo it back verbatim in POST /v1/fyp/events."
                    },
                    "final_score": {
                        "type": "number",
                        "description": "Composite ranking score [0, 1]"
                    },
                    "boost_source": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Which tier applied the community boost - 'subscribed', 'engaged', or null"
                    },
                    "score_recency": {
                        "type": "number"
                    },
                    "score_relevance": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "description": "Semantic relevance to the user's interest vector (null in the global feed)"
                    },
                    "score_engagement": {
                        "type": "number"
                    },
                    "algorithm_version": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Ranking algorithm version that produced this entry, for A/B analysis. Global-sourced entries carry it too (#23); null only for entries written before that change."
                    },
                    "score_credibility": {
                        "type": "number"
                    },
                    "community_boost_applied": {
                        "type": "boolean"
                    }
                },
                "description": "This app's ranking data for the post (the FYP-specific signal).",
                "x-sql-datatype": "object"
            }
        }
    },
    "externalDocs": {
        "url": "https://gitlab.syncad.com/hive/haf_fyp",
        "description": "HAF FYP GitLab repository"
    }
}