{"openapi":"3.1.0","info":{"title":"Mirsad partner API","version":"0.3.0","description":"API for partner systems. Every response contains only the calling organisation's data. Post tracking is live: register an Instagram post or Reel with its creator's username and the campaign's end date, and Mirsad reads its public numbers (views for Reels, likes, comments, the creator's followers) once a day until the day after the campaign ends, from Instagram's Business Discovery (Creator and Business accounts only). A number Instagram does not show (hidden likes, views on a photo) is null, never 0. Creators may also sign in with Instagram (POST /connect-links): Mirsad then reads their own insights (reach, views, saves, shares, interactions, Reels watch time) for their tracked posts once a day, and their stories every hour while they are live; these come as latest_insights / source=ig_login and under /connections/{id}/stories, and are deleted when the creator disconnects. Keys are created in the portal (API keys) and limited to 30 requests a minute. The monitoring endpoints (/mentions, /reports, /metrics, /entities) are planned and answer 501."},"servers":[{"url":"https://api.mirsad.si/v1"}],"security":[{"apiKey":[]}],"paths":{"/tracked-posts":{"post":{"operationId":"registerTrackedPost","description":"Needs the tracking.write scope. Sending the same post again for the same campaign returns the existing record (200), so retries are safe; the same post under another campaign is 409. A post that ended not_found with no readings (wrong username or link) can be sent again and is registered afresh.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedPostRequest"}}}},"responses":{"200":{"description":"Already registered for this campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedPost"}}}},"201":{"description":"Registered (status pending until the first reading, usually within 10 minutes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedPost"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}},"get":{"operationId":"listTrackedPosts","parameters":[{"name":"external_campaign_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/TrackingStatus"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackedPost"}},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/tracked-posts/{id}":{"get":{"operationId":"getTrackedPost","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The post and its latest reading","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedPost"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}},"delete":{"operationId":"stopTrackedPost","description":"Stops tracking (status stopped). Readings are kept. Needs the tracking.write scope.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedPost"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/tracked-posts/{id}/metrics":{"get":{"operationId":"getTrackedPostMetrics","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Inclusive, a date in Baku time."},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Inclusive, a date in Baku time."},{"name":"source","in":"query","schema":{"type":"string","enum":["business_discovery","ig_login"],"default":"business_discovery"},"description":"business_discovery: public readings (Reading). ig_login: the creator's own insights while connected (InsightsReading)."}],"responses":{"200":{"description":"Every reading from that source, oldest first","content":{"application/json":{"schema":{"type":"object","required":["post","source","data"],"properties":{"post":{"$ref":"#/components/schemas/TrackedPost"},"source":{"type":"string","enum":["business_discovery","ig_login"]},"data":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/Reading"},{"$ref":"#/components/schemas/InsightsReading"}]}}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/campaigns/{external_campaign_id}/summary":{"get":{"operationId":"getCampaignSummary","parameters":[{"name":"external_campaign_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Totals over each post's latest reading","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/connect-links":{"post":{"operationId":"createConnectLink","description":"Needs the tracking.write scope and Instagram sign-in set up for the organisation (its own Meta app; ask Mirsad). Returns a one-time link, valid for an hour, to open in the creator's browser (add &lang=az or &lang=ru for the page's language). The creator sees Meta's consent screen for the organisation's app and is sent back to return_url with ?mirsad_connect=connected&connection_id=…&external_creator_id=…&username=…, or ?mirsad_connect=error&reason=denied|account_mismatch|account_in_use|not_professional|permissions_missing|browser_mismatch|failed&external_creator_id=…. Any answer except browser_mismatch uses the link up. return_url must sit under an address the organisation allowed. A sign-in with another account than username is refused (account_mismatch), and so is an account already connected for another creator (account_in_use).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkRequest"}}}},"responses":{"201":{"description":"The link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLink"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/connections":{"get":{"operationId":"listConnections","parameters":[{"name":"external_creator_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/ConnectionStatus"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/connections/{id}":{"get":{"operationId":"getConnection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The connection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}},"delete":{"operationId":"disconnect","description":"Needs tracking.write. Revokes this connection: the token, the account's details and every reading that came from this sign-in (insights, stories) are deleted; public readings stay. A connection.revoked event follows. Earlier sign-ins of the same creator (status replaced or expired) are separate connections and keep their readings until deleted the same way; a removal of the app or a deletion request through Instagram covers all of them.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The connection, revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/connections/{id}/stories":{"get":{"operationId":"getConnectionStories","description":"Stories read while they were live (hourly; Instagram shows a story's insights only for 24 hours), newest first, at most 200. A story with fewer than 5 viewers has no numbers (Instagram withholds them).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Inclusive, posting date in Baku time."},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Inclusive."}],"responses":{"200":{"description":"The connection and its stories","content":{"application/json":{"schema":{"type":"object","required":["connection","data"],"properties":{"connection":{"$ref":"#/components/schemas/Connection"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Story"}}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/mentions":{"get":{"operationId":"listMentions","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Inclusive. The range may not exceed 31 days."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Exclusive."},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"platform","in":"query","schema":{"$ref":"#/components/schemas/Platform"}},{"name":"sentiment","in":"query","schema":{"type":"string","enum":["neg","neu","pos"]}},{"name":"attn_tier","in":"query","schema":{"type":"string","enum":["urgent","soon","watch","none"]}},{"name":"entity_id","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A page of mentions","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Mention"}},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/mentions/{id}":{"get":{"operationId":"getMention","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"One mention","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mention"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/reports":{"get":{"operationId":"listReports","parameters":[{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Sent reports","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReportSummary"}},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/reports/{id}":{"get":{"operationId":"getReport","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A sent report's frozen snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Report"}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/metrics":{"get":{"operationId":"getMetrics","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Inclusive. The range may not exceed 31 days."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Exclusive."},{"name":"interval","in":"query","schema":{"type":"string","enum":["day","week"],"default":"day"}},{"name":"entity_id","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Counts per interval","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MetricPoint"}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/entities":{"get":{"operationId":"listEntities","responses":{"200":{"description":"Entities the organisation tracks","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Entity"}}}}},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}}},"webhooks":{"connection.event":{"post":{"description":"Instagram sign-in changes (connection.connected, connection.expired: Instagram no longer accepts the token and the creator must connect again, readings kept; connection.revoked: the creator removed the app, asked Meta for deletion, or you disconnected them, sign-in data deleted). POSTed to the organisation's notify URL with headers Mirsad-Event-Id and Mirsad-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" under the notify secret Mirsad gave you>. Reject a t older than 5 minutes and de-duplicate on the event id; delivery is at least once, retried with backoff for about a day until you answer 2xx.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionEvent"}}}},"responses":{"2XX":{"description":"Received"}}}},"mention.attention":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received"}}}},"digest.ready":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received"}}}},"report.sent":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Created in the portal (API keys), shown once, hashed at rest, revocable. Scopes: tracking.read, tracking.write."}},"responses":{"Error":{"description":"Error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}},"schemas":{"TrackingStatus":{"type":"string","enum":["pending","active","ended","stopped","deleted","private","not_found"],"description":"pending: registered, not read yet. active: read daily. ended: the final reading after the campaign end date was taken. stopped: stopped through the API. deleted: gone from the creator's posts on two readings in a row. not_found: Instagram has no Creator or Business account with this username (unknown, renamed or a personal account), or the post was not among the creator's recent posts, on two daily readings in a row (until then status_note says 'not found yet'). Register it again with the right username to retry. private: the account is no longer public."},"TrackedPostRequest":{"type":"object","additionalProperties":false,"required":["url","username","external_campaign_id","campaign_ends_on"],"properties":{"url":{"type":"string","format":"uri","description":"https://www.instagram.com/p/<code>/ or /reel/<code>/ (stories are not supported)"},"username":{"type":"string","description":"The creator's Instagram username, with or without @"},"external_campaign_id":{"type":"string","minLength":1,"maxLength":100},"external_creator_id":{"type":["string","null"],"minLength":1,"maxLength":100},"campaign_ends_on":{"type":"string","format":"date","description":"Last day of the campaign (Baku). Today or later, at most 365 days ahead."}}},"Reading":{"type":"object","required":["captured_at","views","likes","comments","followers"],"properties":{"captured_at":{"type":"string","format":"date-time"},"views":{"type":["integer","null"],"description":"Reels only"},"likes":{"type":["integer","null"],"description":"Null when the creator hides likes"},"comments":{"type":["integer","null"]},"followers":{"type":["integer","null"],"description":"The creator's followers at the time of the reading"}}},"TrackedPost":{"type":"object","required":["id","platform","url","username","external_campaign_id","campaign_ends_on","status","latest"],"properties":{"id":{"type":"string","format":"uuid"},"platform":{"type":"string","enum":["instagram"]},"url":{"type":"string","format":"uri"},"username":{"type":"string"},"external_campaign_id":{"type":"string"},"external_creator_id":{"type":["string","null"]},"campaign_ends_on":{"type":"string","format":"date"},"status":{"$ref":"#/components/schemas/TrackingStatus"},"status_note":{"type":["string","null"]},"media_type":{"type":["string","null"],"enum":["reel","post",null]},"posted_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"last_checked_at":{"type":["string","null"],"format":"date-time"},"latest":{"oneOf":[{"$ref":"#/components/schemas/Reading"},{"type":"null"}]},"latest_insights":{"oneOf":[{"$ref":"#/components/schemas/InsightsReading"},{"type":"null"}],"description":"The latest reading from the creator's sign-in, if they are connected"}}},"InsightsReading":{"type":"object","required":["captured_at"],"description":"From the creator's own sign-in (Instagram may lag up to 48 hours). Null when Instagram did not return the metric.","properties":{"captured_at":{"type":"string","format":"date-time"},"views":{"type":["integer","null"]},"reach":{"type":["integer","null"]},"likes":{"type":["integer","null"]},"comments":{"type":["integer","null"]},"saves":{"type":["integer","null"]},"shares":{"type":["integer","null"]},"total_interactions":{"type":["integer","null"]},"avg_watch_time_ms":{"type":["integer","null"],"description":"Reels only"},"total_watch_time_ms":{"type":["integer","null"],"description":"Reels only"}}},"ConnectLinkRequest":{"type":"object","additionalProperties":false,"required":["external_creator_id","return_url","username"],"properties":{"external_creator_id":{"type":"string","minLength":1,"maxLength":100},"return_url":{"type":"string","format":"uri","description":"https, under an address allowed for the organisation"},"username":{"type":"string","description":"The creator's Instagram username (with or without @): only this account may sign in"}}},"ConnectLink":{"type":"object","required":["id","url","external_creator_id","username","expires_at"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"external_creator_id":{"type":"string"},"username":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}}},"ConnectionStatus":{"type":"string","enum":["active","expired","replaced","revoked"],"description":"active: read daily (posts) and hourly (stories). expired: Instagram no longer accepts the token; readings kept; send a new connect link. replaced: the creator (or the same account) signed in again. revoked: disconnected; the sign-in's data is deleted."},"Connection":{"type":"object","required":["id","platform","external_creator_id","status","connected_at"],"properties":{"id":{"type":"string","format":"uuid"},"platform":{"type":"string","enum":["instagram"]},"external_creator_id":{"type":"string"},"status":{"$ref":"#/components/schemas/ConnectionStatus"},"end_reason":{"type":["string","null"],"enum":["reconnected","token_invalid","deauthorized","data_deletion","partner",null]},"ig_user_id":{"type":["string","null"]},"username":{"type":["string","null"],"description":"Null once revoked"},"account_type":{"type":["string","null"],"enum":["creator","business",null]},"scopes":{"type":"array","items":{"type":"string"}},"connected_at":{"type":"string","format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"token_expires_at":{"type":["string","null"],"format":"date-time"},"last_insights_at":{"type":["string","null"],"format":"date-time"},"last_stories_at":{"type":["string","null"],"format":"date-time"}}},"Story":{"type":"object","required":["media_id","readings"],"properties":{"media_id":{"type":"string"},"media_type":{"type":["string","null"],"enum":["image","video",null]},"permalink":{"type":["string","null"]},"posted_at":{"type":["string","null"],"format":"date-time"},"latest":{"oneOf":[{"$ref":"#/components/schemas/StoryReading"},{"type":"null"}]},"readings":{"type":"array","items":{"$ref":"#/components/schemas/StoryReading"}}}},"StoryReading":{"type":"object","required":["captured_at"],"properties":{"captured_at":{"type":"string","format":"date-time"},"reach":{"type":["integer","null"]},"views":{"type":["integer","null"]},"replies":{"type":["integer","null"]},"link_clicks":{"type":["integer","null"]},"shares":{"type":["integer","null"]},"total_interactions":{"type":["integer","null"]},"follows":{"type":["integer","null"]},"profile_visits":{"type":["integer","null"]}}},"ConnectionEvent":{"type":"object","required":["id","type","created_at","data"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["connection.connected","connection.expired","connection.revoked"]},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","required":["connection"],"properties":{"connection":{"$ref":"#/components/schemas/Connection"},"replaces":{"type":"array","items":{"type":"string","format":"uuid"}}}}}},"CampaignSummary":{"type":"object","required":["external_campaign_id","posts","posts_with_readings","by_status","totals","posts_with_hidden_likes","data","data_complete"],"properties":{"external_campaign_id":{"type":"string"},"posts":{"type":"integer"},"posts_with_readings":{"type":"integer"},"by_status":{"type":"object","additionalProperties":{"type":"integer"}},"totals":{"type":"object","properties":{"views":{"type":"integer"},"likes":{"type":"integer"},"comments":{"type":"integer"}}},"posts_with_hidden_likes":{"type":"integer","description":"Posts whose likes are hidden and therefore not in totals.likes"},"last_captured_at":{"type":["string","null"],"format":"date-time"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackedPost"},"description":"The first 500 posts, oldest first; totals always cover every post"},"data_complete":{"type":"boolean"},"insights":{"type":"object","description":"Sums of the latest sign-in reading of each post that has one (also from connections that have since expired or been replaced; revoked ones have none)","properties":{"posts_with_insights":{"type":"integer"},"totals":{"type":"object","properties":{"views":{"type":"integer"},"reach":{"type":"integer"},"saves":{"type":"integer"},"shares":{"type":"integer"},"total_interactions":{"type":"integer"}}},"last_captured_at":{"type":["string","null"],"format":"date-time"}}}}},"Platform":{"type":"string","enum":["facebook","instagram","tiktok","telegram","youtube","news","x","linkedin"]},"Mention":{"type":"object","required":["id","platform","object_type","url","text","posted_at"],"properties":{"id":{"type":"string","format":"uuid"},"platform":{"$ref":"#/components/schemas/Platform"},"object_type":{"type":"string","enum":["post","comment","video","reel","article","message"]},"parent_id":{"type":["string","null"],"format":"uuid"},"url":{"type":"string","format":"uri"},"text":{"type":"string"},"author_handle":{"type":["string","null"]},"posted_at":{"type":"string","format":"date-time"},"posted_at_precision":{"type":"string","enum":["second","minute","day"]},"deleted":{"type":"boolean"},"classification":{"type":["object","null"],"description":"Null until the item is classified.","properties":{"entity_id":{"type":"string","format":"uuid"},"prompt_version":{"type":"string"},"relevant":{"type":"boolean"},"sentiment":{"type":"string","enum":["neg","neu","pos"]},"attn_tier":{"type":"string","enum":["urgent","soon","watch","none"]},"attn_why":{"type":["string","null"]},"needs_reply":{"type":"boolean"},"summary_en":{"type":"string"},"confidence":{"type":"number","minimum":0,"maximum":1}}},"metrics":{"type":["object","null"],"properties":{"captured_at":{"type":"string","format":"date-time"},"likes":{"type":["integer","null"]},"comments":{"type":["integer","null"]},"shares":{"type":["integer","null"]},"views":{"type":["integer","null"]}}}}},"ReportSummary":{"type":"object","required":["id","period_start","period_end","sent_at"],"properties":{"id":{"type":"string","format":"uuid"},"period_start":{"type":"string","format":"date"},"period_end":{"type":"string","format":"date"},"sent_at":{"type":"string","format":"date-time"}}},"Report":{"allOf":[{"$ref":"#/components/schemas/ReportSummary"},{"type":"object","properties":{"snapshot":{"type":"object"},"narrative":{"type":["string","null"]},"metrics_as_of":{"type":"string","format":"date-time"}}}]},"MetricPoint":{"type":"object","required":["period_start","mentions"],"properties":{"period_start":{"type":"string","format":"date"},"mentions":{"type":"integer"},"neg":{"type":"integer"},"neu":{"type":"integer"},"pos":{"type":"integer"},"attention":{"type":"integer"},"reach":{"type":["integer","null"],"description":"Null where the platform gives no reach. Never 0 for unknown."}}},"Entity":{"type":"object","required":["id","name","relation"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"relation":{"type":"string","enum":["own","competitor"]}}},"WebhookEvent":{"type":"object","description":"Signed with HMAC-SHA256 over `${timestamp}.${body}` in the Mirsad-Signature header; carries Mirsad-Timestamp and an idempotency key.","required":["id","event","created_at","data"],"properties":{"id":{"type":"string","description":"Idempotency key"},"event":{"type":"string","enum":["mention.attention","digest.ready","report.sent"]},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","properties":{"mention_id":{"type":"string"},"report_id":{"type":"string"},"summary":{"type":"string"}}}}}}}}