{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/api-reference/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["api-container","endpoint"]},"redocly_category":"API Reference","searchWeight":120,"type":"markdown"},"seo":{"title":"Shared Thread Read State","description":"Complete API reference and documentation for Freddy AI-powered backend system by Aitronos","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"shared-thread-read-state","__idx":0},"children":["Shared Thread Read State"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read and advance ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["your own"]}," reading position on a shared thread, so a client can put you back where you stopped and draw an \"N new messages\" divider instead of always landing at the newest message."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The owner read routes are ownership-scoped and return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["404"]}," for a share-link recipient. These are the slug-scoped equivalents. Authorization is the same as reading the thread's messages — the slug must resolve for you — so both a view-only visitor and a joined participant can use them."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The marker is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["per-viewer"]},": it is keyed on the authenticated user and is never shared between people. It is also the same read cursor the thread's unread badge uses, so marking read here clears the thread's unread indicator everywhere rather than keeping a separate position that drifts."]},{"$$mdtype":"Tag","name":"ApiContainer","attributes":{},"children":[{"$$mdtype":"Tag","name":"Endpoint","attributes":{"method":"GET","path":"https://api.aitronos.com/v1/shared-threads/{slug}/read-state"},"children":[]},{"$$mdtype":"Tag","name":"Endpoint","attributes":{"method":"PUT","path":"https://api.aitronos.com/v1/shared-threads/{slug}/read-state"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authentication","__idx":1},"children":["Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Requires ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["user"]}," authentication (Bearer token). An API key has no recipient identity and is rejected with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["403"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"path-parameters","__idx":2},"children":["Path Parameters"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["slug"]}]}," string ",{"$$mdtype":"Tag","name":"em","attributes":{"style":{"color":"#ef4444 !important","fontWeight":"600 !important","fontStyle":"normal !important"}},"children":["required"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The share-link slug, from the share URL."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"request-body-put-only","__idx":3},"children":["Request Body (PUT only)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["last_read_message_id"]}]}," string ",{"$$mdtype":"Tag","name":"em","attributes":{"style":{"color":"#ef4444 !important","fontWeight":"600 !important","fontStyle":"normal !important"}},"children":["required"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The newest message you have now read. It must be a message this thread actually shows you; an unknown, deleted, hidden, or other-thread id returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["404"]}," rather than being stored, because a marker pointing at nothing is a restore position you could never clear."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The write is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["monotonic"]},": a message older than your stored marker leaves the stored one in place. Scroll-driven calls, a second tab, and late retries can therefore never rewind your progress."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"returns","__idx":4},"children":["Returns"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both methods return the read state as it stands ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["after"]}," the call, so a client never has to re-read to learn the outcome."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["last_read_message_id"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["string | null"]}," — the message you last read. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," is normal: you have never opened the thread, or your cursor was set by a whole-thread mark-read that named no message."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["last_read_at"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["string | null"]}," — timestamp of the cursor. Set whenever any read state exists, even when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["last_read_message_id"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["unread_count"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["integer"]}," — messages after the cursor you have not seen. Counts only rows the transcript renders, and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["never your own messages"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["first_unread_message_id"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["string | null"]}," — the message to draw the divider above, and to pass as the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["around"]}," anchor when jumping there. Computed server-side, so a deleted or invisible row right after the cursor cannot become a dead anchor. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," exactly when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unread_count"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["unread_count_capped"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["boolean"]}," — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," when more messages are unread than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unread_count"]}," reports (the count stops at 999 so the scan stays bounded). Render ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["999+"]}," rather than an exact number when set."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"code-examples","__idx":5},"children":["Code Examples"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"curl","__idx":6},"children":["cURL"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Where did I stop reading?\ncurl -X GET \"https://api.aitronos.com/v1/shared-threads/abc123slug/read-state\" \\\n  -H \"Authorization: Bearer YOUR_TOKEN\"\n\n# I have now read up to this message.\ncurl -X PUT \"https://api.aitronos.com/v1/shared-threads/abc123slug/read-state\" \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"last_read_message_id\": \"msg_abc123\"}'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"python-sdk","__idx":7},"children":["Python SDK"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from aitronos import Aitronos\n\nclient = Aitronos(api_key=\"\", token=\"YOUR_TOKEN\")\n\nstate = client.threads.shared.get_read_state(slug=\"abc123slug\")\nif state.unread_count:\n    print(f\"{state.unread_count} new since {state.last_read_at}\")\n    print(f\"divider goes above {state.first_unread_message_id}\")\n\nclient.threads.shared.set_read_state(\n    slug=\"abc123slug\",\n    last_read_message_id=\"msg_abc123\",\n)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"javascript","__idx":8},"children":["JavaScript"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const state = await (\n  await fetch(\n    \"https://api.aitronos.com/v1/shared-threads/abc123slug/read-state\",\n    { headers: { Authorization: \"Bearer YOUR_TOKEN\" } },\n  )\n).json();\n\nawait fetch(\n  \"https://api.aitronos.com/v1/shared-threads/abc123slug/read-state\",\n  {\n    method: \"PUT\",\n    headers: {\n      Authorization: \"Bearer YOUR_TOKEN\",\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({ last_read_message_id: \"msg_abc123\" }),\n  },\n);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"response-examples","__idx":9},"children":["Response Examples"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"success--you-have-unread-messages-200","__idx":10},"children":["Success — you have unread messages (200)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"last_read_message_id\": \"msg_abc123\",\n  \"last_read_at\": \"2026-08-05T20:07:04Z\",\n  \"unread_count\": 12,\n  \"first_unread_message_id\": \"msg_def456\",\n  \"unread_count_capped\": false\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"success--never-opened-200","__idx":11},"children":["Success — never opened (200)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Open at the bottom with no divider. This is a normal state, not an error."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"last_read_message_id\": null,\n  \"last_read_at\": null,\n  \"unread_count\": 0,\n  \"first_unread_message_id\": null,\n  \"unread_count_capped\": false\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error--the-message-is-not-part-of-this-thread-404","__idx":12},"children":["Error — the message is not part of this thread (404)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"The requested resource could not be found.\",\n    \"system_message\": \"That message is not part of this thread\",\n    \"type\": \"client_error\",\n    \"status\": 404,\n    \"details\": { \"last_read_message_id\": \"msg_ghost\" },\n    \"trace_id\": \"...\",\n    \"timestamp\": \"...\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error--unknown--unauthorized--expired-link-404","__idx":13},"children":["Error — unknown / unauthorized / expired link (404)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same uniform deny as opening the link — it never reveals whether the link or thread exists."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"The requested resource could not be found.\",\n    \"system_message\": \"Share link not found\",\n    \"type\": \"client_error\",\n    \"status\": 404,\n    \"details\": {},\n    \"trace_id\": \"...\",\n    \"timestamp\": \"...\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-resources","__idx":14},"children":["Related Resources"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/shared-threads/get-shared-thread-messages"},"children":["Get Shared Thread Messages"]}," — Read the transcript the marker points into"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/shared-threads/mark-shared-thread-read"},"children":["Mark Shared Thread Read"]}," — Clear the whole thread at once, without naming a message"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/shared-threads/get-shared-thread"},"children":["Get Shared Thread"]}," — Open the share link"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/shared-threads/join-shared-thread"},"children":["Join Shared Thread"]}," — Join a live-shared thread"]}]}]}]},"headings":[{"value":"Shared Thread Read State","id":"shared-thread-read-state","depth":1},{"value":"Authentication","id":"authentication","depth":2},{"value":"Path Parameters","id":"path-parameters","depth":2},{"value":"Request Body (PUT only)","id":"request-body-put-only","depth":2},{"value":"Returns","id":"returns","depth":2},{"value":"Code Examples","id":"code-examples","depth":2},{"value":"cURL","id":"curl","depth":3},{"value":"Python SDK","id":"python-sdk","depth":3},{"value":"JavaScript","id":"javascript","depth":3},{"value":"Response Examples","id":"response-examples","depth":2},{"value":"Success — you have unread messages (200)","id":"success--you-have-unread-messages-200","depth":3},{"value":"Success — never opened (200)","id":"success--never-opened-200","depth":3},{"value":"Error — the message is not part of this thread (404)","id":"error--the-message-is-not-part-of-this-thread-404","depth":3},{"value":"Error — unknown / unauthorized / expired link (404)","id":"error--unknown--unauthorized--expired-link-404","depth":3},{"value":"Related Resources","id":"related-resources","depth":2}],"frontmatter":{"title":"Shared Thread Read State","seo":{"title":"Shared Thread Read State"}},"lastModified":"2026-10-26T18:32:31.653Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/api-reference/shared-threads/shared-thread-read-state","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}