{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://api.quanor.com/api/docs/stream/feed_frame.v1.schema.json","$comment":"The frames of the feed, version 1. Validate a frame against this schema's root; feed_frame.v1.json holds an example of each item, hidden and withdrawn frame an API key is sent; the reference shows its part, processing and processing_resolved frames. Every object admits fields it does not name: a reader ignores what it does not know. A field the stream sets that names a kind, a state, a reason or a code (head.route, a story's treatment, op, access) lists the values version 1 names and admits any other string: a new value is additive, and a reader treats one it does not know as unknown. A frame whose type version 1 names neither here nor in feed_stream.v1.schema.json is valid (unknown_frame), to be ignored; one whose type it names is held to that type's schema. A client generated from an earlier copy of this file is not updated by a value or a type it admits now: generate from the served copy.","title":"A feed frame","allOf":[{"$ref":"#/$defs/listener_fields"}],"oneOf":[{"$ref":"#/$defs/item"},{"$ref":"#/$defs/part_frame"},{"$ref":"#/$defs/hidden"},{"$ref":"#/$defs/withdrawn"},{"$ref":"#/$defs/processing"},{"$ref":"#/$defs/processing_resolved"},{"$ref":"#/$defs/unknown_frame"}],"$defs":{"moment":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{6}Z$","description":"UTC, to the microsecond, Z: 2026-10-08T06:00:12.300000Z."},"feed_cursor":{"type":"string","pattern":"^[1-9][0-9]{0,9}\\.(0|[1-9][0-9]{0,18})$","description":"<epoch>.<seq>: every change at or below seq is accounted for. Resume from it. Two cursors order by epoch, then by seq, each as an integer (seq at most 9223372036854775807): 1.9 is before 1.10."},"listener_fields":{"description":"What the stream adds to every frame it sends one listener; a /v1 read carries `m` alone, and only on a catch-up, which, asked with include_event_ids, names beside it the event a webhook endpoint of the key is sent for the frame (`event_type`, `event_id`).","type":"object","properties":{"n":{"type":"integer","minimum":1,"description":"This connection's counter, contiguous: an integer of up to 64 bits."},"cursor":{"$ref":"#/$defs/feed_cursor","description":"The feed cursor after this frame: every position at or below it is accounted for once the frame is sent. A watermark, not the frame's position: it can be below, at or above the frame's seq. Keep the last one received and resume from it."},"m":{"type":"array","items":{"type":"string"},"description":"The mirrors (selections) the frame is for."},"access_version":{"type":"string","description":"The listener's access when the frame was sent."},"event_type":{"type":"string","pattern":"^[a-z_]+\\.[a-z_]+$","description":"/v1/feed/since alone, asked with include_event_ids: the type of the event a webhook endpoint of the key is sent for the frame (/v1/event-types)."},"event_id":{"type":"string","pattern":"^evt_[0-9a-hjkmnp-tv-z]{26}$","description":"/v1/feed/since alone, asked with include_event_ids: that event's id; read in the endpoint's lang and without page filters, the id the endpoint is pushed for the change (a refetch it is pushed is an event of its own)."}}},"company":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"integer","minimum":1},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"short_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"symbol":{"anyOf":[{"type":"string"},{"type":"null"}]},"listed_exchange":{"anyOf":[{"type":"string"},{"type":"null"}]},"market_exchange_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"market_exchange_mic":{"anyOf":[{"type":"string","description":"ISO 10383, e.g. XSTO."},{"type":"null"}]},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"report":{"type":"object","required":["period"],"properties":{"period":{"type":"string"},"period_end":{"anyOf":[{"type":"string"},{"type":"null"}]},"annual":{"type":"boolean"},"document_read":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"description":"The report a report story is about: its period as stored (2026Q3, 2026H1, 2026-9M, FY2025), the last day of that period, whether it is a formal annual report, and whether the report document was read (false for a version written from the announcement's text alone, which a later version completes)."},"head":{"description":"What an announcement shows around its parts.","type":"object","required":["edition_ids","title","inside_information"],"properties":{"announcement_id":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}]},"edition_ids":{"type":"array","items":{"type":"integer","minimum":1}},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"inside_information":{"anyOf":[{"type":"boolean","description":"true flagged, false classified and not, null not known yet."},{"type":"null"}]},"announcement_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"received_at":{"$ref":"#/$defs/moment"},"route":{"description":"The values version 1 names, any other string, or null while not known; a reader treats one it does not know as unknown.","anyOf":[{"enum":["press_release","report","annual_report"]},{"type":"string"},{"type":"null"}]},"report":{"anyOf":[{"$ref":"#/$defs/report"},{"type":"null"}]},"first_flash_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]},"in_focus_at":{"description":"When the announcement went In focus: present only beside `flags.in_focus`, which an API key is not sent, and only where the moment is known. The moment alone — never a price, a move or a direction.","$ref":"#/$defs/moment"}}},"pre_earnings":{"description":"What a pre-earnings analysis (item_kind pre_earnings_analysis) shows of itself: read it whole on /v1/analyses/PRE_EARNINGS/{analysis_id}. A teaser shows none of it.","type":"object","properties":{"analysis_id":{"type":"integer","minimum":1},"report_period":{"type":"string","description":"The report period it is about, as written: Q3 2026."},"title":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"flash_part":{"description":"One flash row: Quanor's first reading of the announcement. The list level (`api_list`) carries the first row's headline and relevance alone.","type":"object","required":["kind","part_id","part_version","flash_id","edition_id","created_at","headline","relevance"],"properties":{"kind":{"const":"flash"},"part_id":{"type":"string"},"part_version":{"type":"integer","description":"An opaque integer of up to 64 bits (a flash row's is in the order of 10^15): of two copies of a part the higher is the later. Compare, never compute."},"flash_id":{"type":"integer","minimum":1},"edition_id":{"type":"integer","minimum":1},"created_at":{"$ref":"#/$defs/moment"},"flash_type":{"anyOf":[{"type":"string"},{"type":"null"}]},"headline":{"anyOf":[{"type":"string"},{"type":"null"}]},"relevance":{"anyOf":[{"type":"integer","minimum":1,"maximum":5,"description":"null for a row that stored none."},{"type":"null"}]},"sentiment":{"anyOf":[{"type":"integer"},{"type":"null"}]},"sentiment_confidence":{"anyOf":[{"type":"integer"},{"type":"null"}]},"corrects":{"type":"array","items":{"type":"string"},"description":"The part ids this row corrects."}}},"story_part":{"description":"The announcement's story: read it on /v1/analyses/STORY/{story_id}, a report story — one whose report is set — on /v1/analyses/REPORT/{story_id}. In the app's teasers it carries its headline, short lead and kicker alone; an API key's teaser carries no parts.","type":"object","required":["kind","part_id","part_version","story_id","lang","headline"],"properties":{"kind":{"const":"story"},"part_id":{"type":"string"},"part_version":{"type":"integer","description":"An opaque integer of up to 64 bits (a flash row's is in the order of 10^15): of two copies of a part the higher is the later. Compare, never compute."},"story_id":{"type":"integer","minimum":1},"version":{"type":"integer"},"treatment":{"description":"full, a Story, or brief, a Brief: the values version 1 names; a reader treats one it does not know as unknown.","anyOf":[{"enum":["full","brief"]},{"type":"string"}]},"corrected":{"type":"boolean"},"lang":{"type":"string"},"reason":{"type":"string"},"relevance":{"anyOf":[{"type":"integer"},{"type":"null"}]},"inside_information":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"first_published_at":{"$ref":"#/$defs/moment"},"version_published_at":{"$ref":"#/$defs/moment"},"headline":{"type":"string"},"lede_first_sentence":{"anyOf":[{"type":"string"},{"type":"null"}]},"lede":{"anyOf":[{"type":"string"},{"type":"null"}]},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"report":{"anyOf":[{"$ref":"#/$defs/report"},{"type":"null"}]},"kicker":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A report story's kicker in the served language, which a row shows in place of Story or Brief (\"Report · Q3 2026\", the period as the company names it); null for every other story."}}},"audio_part":{"type":"object","required":["kind","part_id","part_version","media_id"],"properties":{"kind":{"const":"audio"},"part_id":{"type":"string"},"part_version":{"type":"integer","description":"An opaque integer of up to 64 bits (a flash row's is in the order of 10^15): of two copies of a part the higher is the later. Compare, never compute."},"media_id":{"type":"integer","minimum":1},"duration_s":{"anyOf":[{"type":"integer"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"$ref":"#/$defs/moment"}}},"part":{"description":"One part of an item, by its kind. A part kind this contract does not name is ignored.","type":"object","required":["kind","part_id","part_version"],"properties":{"kind":{"type":"string"},"part_id":{"type":"string"},"part_version":{"type":"integer","description":"An opaque integer of up to 64 bits (a flash row's is in the order of 10^15): of two copies of a part the higher is the later. Compare, never compute."}},"allOf":[{"if":{"properties":{"kind":{"const":"flash"}}},"then":{"$ref":"#/$defs/flash_part"}},{"if":{"properties":{"kind":{"const":"story"}}},"then":{"$ref":"#/$defs/story_part"}},{"if":{"properties":{"kind":{"const":"audio"}}},"then":{"$ref":"#/$defs/audio_part"}}]},"item":{"description":"A whole-item snapshot: replaces the item held when its seq is higher. An announcement carries head and parts, a pre-earnings analysis pre_earnings. An item kind this contract does not name is ignored.","type":"object","required":["type","op","feed_item_id","item_kind","seq","first_seq","sort_at","published_at","changes","access","lang","company"],"properties":{"type":{"const":"item"},"op":{"description":"The values version 1 names; a reader treats one it does not know as unknown.","anyOf":[{"enum":["new","changed"]},{"type":"string"}]},"feed_item_id":{"type":"string"},"item_kind":{"type":"string"},"seq":{"type":"integer","minimum":1,"description":"A position of the feed: an integer of up to 64 bits. Compare, never compute."},"first_seq":{"type":"integer","minimum":1,"description":"The item's first position of the feed: an integer of up to 64 bits."},"sort_at":{"$ref":"#/$defs/moment"},"published_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]},"changes":{"type":"array","items":{"type":"string"},"description":"What changed, when the builder knew: a hint. The names version 1 sends are feed_frame.v1.json's (changes); a reader ignores one it does not know."},"access":{"description":"The values version 1 names; a reader treats one it does not know as unknown.","anyOf":[{"enum":["full","teaser"]},{"type":"string"}]},"lang":{"type":"string"},"company":{"anyOf":[{"$ref":"#/$defs/company"},{"type":"null"}]},"head":{"$ref":"#/$defs/head"},"parts":{"type":"array","items":{"$ref":"#/$defs/part"}},"superseded_parts":{"type":"array","items":{"type":"string"},"description":"Part ids to drop at once."},"flags":{"type":"object","properties":{"corrected":{"type":"boolean"},"in_focus":{"type":"boolean"}}},"corrected_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]},"flash_count":{"type":"integer","minimum":0,"description":"The list level: how many flash rows."},"pre_earnings":{"$ref":"#/$defs/pre_earnings"}},"allOf":[{"if":{"properties":{"item_kind":{"const":"announcement"},"access":{"const":"full"}},"required":["item_kind","access"]},"then":{"required":["head","parts","flags","corrected_at"]}},{"if":{"properties":{"item_kind":{"const":"pre_earnings_analysis"},"access":{"const":"full"}},"required":["item_kind","access"]},"then":{"required":["pre_earnings","parts"],"properties":{"pre_earnings":{"required":["analysis_id","report_period","title"]}}}}]},"part_frame":{"description":"A flash row ahead of its item's next snapshot (a provisional part): upsert it by part_id and part_version. It has no seq, being at no position of the feed; the stream adds its cursor as to every frame.","type":"object","required":["type","feed_item_id","item_kind","sort_at","part"],"properties":{"type":{"const":"part"},"feed_item_id":{"type":"string"},"item_kind":{"type":"string"},"sort_at":{"$ref":"#/$defs/moment"},"published_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]},"part":{"$ref":"#/$defs/part"},"provisional":{"type":"boolean"},"head":{"$ref":"#/$defs/head"},"company":{"anyOf":[{"$ref":"#/$defs/company"},{"type":"null"}]},"lang":{"type":"string"}}},"hidden":{"description":"No longer shown to this listener (its access, the company, the selection) or, in a catch-up read with filters, no longer passing them: drop the item.","type":"object","required":["type","feed_item_id","seq"],"properties":{"type":{"const":"hidden"},"feed_item_id":{"type":"string"},"seq":{"type":"integer","minimum":1,"description":"A position of the feed: an integer of up to 64 bits. Compare, never compute."}}},"withdrawn":{"description":"Retracted: merged into, or replaced by, replaced_by (null: gone, replaced by an item this listener may not see, or replaced by one not in the feed yet). A listener who may no longer see the item's company is sent hidden at the withdrawal's seq instead.","type":"object","required":["type","feed_item_id","seq","replaced_by"],"properties":{"type":{"const":"withdrawn"},"feed_item_id":{"type":"string"},"seq":{"type":"integer","minimum":1,"description":"A position of the feed: an integer of up to 64 bits. Compare, never compute."},"replaced_by":{"description":"The feed_item_id of the item this one was merged into or replaced by, named only to a listener who may see that item's company; null: gone, or replaced by an item this listener may not see.","anyOf":[{"type":"string"},{"type":"null"}]}}},"processing":{"description":"An edition the flash lane is working on (with can_pending): its item is the one whose head.edition_ids holds edition_id.","type":"object","required":["type","edition_id"],"properties":{"type":{"const":"processing"},"edition_id":{"type":"integer","minimum":1},"company":{"type":"object"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"route":{"anyOf":[{"type":"string"},{"type":"null"}]},"tier":{"anyOf":[{"type":"string"},{"type":"null"}]},"expected_relevance":{"anyOf":[{"type":"number"},{"type":"null"}]},"dispatched_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]},"published_at":{"anyOf":[{"$ref":"#/$defs/moment"},{"type":"null"}]}}},"processing_resolved":{"description":"A processing row settled: drop it.","type":"object","required":["type","edition_id","resolution"],"properties":{"type":{"const":"processing_resolved"},"edition_id":{"type":"integer","minimum":1},"resolution":{"type":"string","description":"How it settled, in a word such as flashed or expired: drop the row whatever it says."},"canonical_edition_id":{"type":"integer","minimum":1}}},"unknown_frame":{"description":"A frame of a type version 1 does not name: ignore it. Its type is none of the frames' or the stream's messages', either way (feed_stream.v1.schema.json), so a frame of a named type that does not hold to that type's schema is invalid rather than unknown.","type":"object","required":["type"],"properties":{"type":{"type":"string","not":{"enum":["item","part","hidden","withdrawn","processing","processing_resolved","ready","selection_ack","through","catch_up","resync","access_changed","status","refetch","reconnect","ping","pong","subscribe","selection"]}}}}}}