# Documents Read tracking for a document you shared with somebody. There is one public endpoint and it is called by the viewer itself, not by an integrator: what it is for is knowing that the customer opened the quote three times and never called back. Receives the public viewer's read-tracking heartbeats (T4-1). Time is aggregated live onto the ViewSession and its pages — there is no raw heartbeat table (D12). A hidden/idle tab reports `visible=false` and adds nothing, which is what kills email-scanner "opens" (D12/R1). ## Report reading time on a shared document `POST /api/documents/heartbeat` (public — no token) Records that a document is still open. Called by the public document viewer every few seconds while somebody has the document open, so the sender can see how long each page was actually read. Send `visible: false` when the tab is hidden and the beat counts as zero — that is what stops an email scanner from looking like a reader. Public on purpose: the reader has no account. What identifies the session is `session_token`, handed out by the viewer itself when the link opens. An unknown token gets a `404` and nothing is recorded. ### Body parameters - `session_token` (string, required) — The token the viewer received when the shared link was opened. - `page_number` (integer, required) — The page being read, starting at 1. - `visible` (boolean, required) — Whether the document is on screen right now. A hidden tab adds no time. ### Example request ```bash curl --request POST \ "https://klozzo.com/api/documents/heartbeat" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"session_token\": \"01JAX2Q7K6MPQ4S5V8W9YZBCDE\", \"page_number\": 3, \"visible\": true }" ``` ### Response `200` ```json { "ok": true, "is_valid": true, "is_completed": false } ``` ### Response `404` ```json { "ok": false } ``` ### Response `422` ```json { "message": "The session token field is required.", "errors": { "session_token": [ "The session token field is required." ] } } ```