{"openapi":"3.1.0","info":{"title":"Iridium - LinkedIn for AI Agents","description":"Iridium gives AI agents live access to LinkedIn, the ability to act on it, and a record of everything they do.","version":"0.1.0"},"paths":{"/mcp/tools/list":{"get":{"tags":["mcp"],"summary":"List Tools","description":"The Iridium tools available to this account, filtered by what the user has enabled.\n\nUseful for answering \"what can you do?\" without guessing. A tool absent from this list is\nswitched off for this account, so do not offer it.\n\nReturns `capabilities`, the handful of jobs Iridium does, and `tools`, the flat list behind\nthem. Lead with the capabilities when answering the user. Read `tools` when picking the call.","operationId":"list_tools","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/search_prospects":{"post":{"tags":["mcp"],"summary":"Search Prospects","description":"The cheapest people search there is: ONE LinkedIn action, no profile opened, no badge\nchecked. Reads only, drafts nothing.\n\nIt is not specific to any signal. It returns whoever matches the query and location —\nthat is all it knows. `hiring_only` / `open_to_work_only` do NOT filter and do NOT\nconfirm anything: they only REORDER the same results using hints in the headline and\ntitle text, which is a guess, not the badge. Someone with the real #hiring badge and a\nplain headline sorts last; someone who typed \"hiring\" in their headline and has no badge\nsorts first.\n\nUSE THIS to browse cheaply, or to get a long list to narrow by hand.\n\nDO NOT USE when the answer has to be TRUE about a badge — \"people who are actually\nhiring\", \"who is really open to work\". This tool cannot know: the badge lives only on the\nfull profile. Use find_prospects, which opens profiles and tells you which candidates it\nconfirmed.\n\nDO NOT USE when the user wants messages written. discover_prospects searches and drafts a\nconnection note for each person in one call.\n\nResults are read live from LinkedIn at the time of the call.\n\nArgs:\n    hiring_only / open_to_work_only: the profile BADGE (is_hiring / is_open_to_work), not\n        post text. The badge is never in a people-search result — it lives only on the\n        full profile (SEARCH.md §7.1/§7.4) — so these do NOT filter; an earlier version\n        faked the filter out of headline text and returned a near-empty list for almost\n        every real query. Instead they SORT by how likely each result is to carry the\n        badge, from free signals already in the search result (headline phrasing, an empty\n        current work history). Nothing is ever dropped — see `note` in the response. This\n        tool opens no profiles, so it never confirms a badge: cost 0 touches, certainty 0.\n        To confirm, use find_prospects with max_touches (1 touch each).\n    posted_recently: neither badge nor post text — an ACTIVITY filter (\"posted at all in\n        the past 30 days\", regardless of what about). Free at search time, needs a Sales\n        Navigator seat; on any other plan the search runs without it and `tier_note` says\n        so. Default false.\n    open_to_work: the profile BADGE, confirmed for free at search time by LinkedIn's own\n        \"Open to work\" spotlight — applied before results come back, so 0 profile reads.\n        Needs a full Recruiter seat (Recruiter Lite has no such spotlight); on any other\n        plan the search runs without it and `tier_note` says so. Default false. Note this\n        is the same badge as open_to_work_only above; the difference is that this one is\n        CONFIRMED by LinkedIn and free, while that one is only a guess at the ordering.","operationId":"search_prospects","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchProspectsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/find_prospects":{"post":{"tags":["mcp"],"summary":"Find Prospects","description":"Find people on LinkedIn and, optionally, verify and write to a few of them — one tool that\nspends only what the filters you asked for actually cost.\n\nUSE THIS as the default for any \"find me people who...\" request. It searches once, narrows\nfor free on the fields the search already returned, and opens full profiles only for the\nnumber you allow, best candidates first.\n\n**This is the tool that answers \"who actually HAS the #hiring badge / the #opentowork\nbadge\".** Set `hiring` or `open_to_work` and give it `max_touches`: it orders the bucket\nby the free signals most likely to carry the badge, opens that many profiles — the only\nplace the badge exists — and tells you which candidates it CONFIRMED. Cost is 1 profile\ntouch per candidate confirmed, except that a full Recruiter seat confirms \"open to work\"\nfor free at search time. Read `confirmed` and each candidate's `confirmed` / `badges` to\nsee who is verified; the rest of the bucket is unverified and the response says so.\n\nFour phases, and you control where it stops:\n  max_touches=0 (default)  search + free narrowing only. No profile is opened, nothing is\n                           charged against the daily profile budget. This is a browse.\n  max_touches=N            additionally opens up to N full profiles (profile AND posts\n                           together, in one pass), best-first, to CONFIRM the badge\n                           filters. Already-known people are served from cache for free\n                           and never counted against N.\n  want_notes=true          additionally drafts a connection note for the confirmed\n                           candidates, reusing profiles already read — no extra LinkedIn\n                           reads, ever. Nothing sends; drafts land in list_drafts.\n\n\"#hiring\" and \"#opentowork\" mean TWO DIFFERENT THINGS on LinkedIn, and which one the user\nmeans decides what the search costs:\n\n  BADGE      the frame on someone's PROFILE (is_hiring / is_open_to_work). It is NOT in\n             any people-search result and NOT in their posts. Seeing it means opening\n             that person's profile — one profile touch each — with a single exception:\n             a full Recruiter seat can filter \"open to work\" at search time for free.\n             This tool's `hiring` and `open_to_work` flags mean the BADGE.\n  POST TEXT  the hashtag someone typed into a POST. Free to search, opens nobody's\n             profile, and says nothing about their badge. That is search_posts_by_topic,\n             not this tool — \"who POSTED about hiring\" is a different question from\n             \"who HAS the hiring badge\", and they return different people.\n\nHonesty contract, worth relaying to the user: `checked` is how many profiles this call\nactually opened, `cached` how many were reused free, `confirmed` how many really carry the\nbadge, and `count - checked - cached` are candidates NOBODY has verified. When the daily\nbudget is spent, the candidate list still comes back with `budget_exhausted` set — the\ntool never returns an empty list to mean \"out of budget\", and never silently stops early.\n\nArgs:\n    query: what to search for, e.g. \"ML engineer\" or \"Head of Growth\".\n    location: plain-text location, e.g. \"San Francisco\" or \"United States\".\n    limit: bucket size to search for (1-100; classic LinkedIn plans return at most 10).\n    company / headline_keywords / exclude_headline_keywords / network_distance: free\n        filters applied to the search results themselves — they cost nothing, so use them\n        freely to cut the list before spending touches. Any-of within a filter, all-of\n        across filters. network_distance: DISTANCE_1 (direct connections), DISTANCE_2, DISTANCE_3.\n    hiring: set this to find people who really have the #hiring profile BADGE (not post\n        text). It is the only way to get that answer: no LinkedIn plan can filter on the\n        badge at search time, so candidates are ORDERED by the free signals that correlate\n        with it and then CONFIRMED by opening profiles — 1 touch per candidate confirmed,\n        on every tier, capped by max_touches. The response marks each confirmed candidate\n        and counts them in `confirmed`. With max_touches=0 nothing is confirmed and this\n        flag only changes the order. For \"who POSTED about hiring\" use\n        search_posts_by_topic instead: free, and a different question with different people.\n    open_to_work: set this to find people who really have the #opentowork profile BADGE\n        (not post text). On a full Recruiter seat LinkedIn confirms it at search time for\n        free (its own spotlight, 0 touches, results arrive pre-filtered); on every other\n        plan — Recruiter Lite included — it falls back to order-then-confirm at 1 touch\n        per candidate confirmed, capped by max_touches. The tool picks whichever route\n        this account can actually use, and the response says which candidates are\n        confirmed either way.\n    posted_recently: neither badge nor post text — an ACTIVITY filter (\"posted at all in\n        the past 30 days\", regardless of what about). Free at search time, Sales Navigator\n        seats only; on other plans the search runs without it and `tier_note` says so.\n    max_touches: how many profiles this call may open BEFORE answering (0-10, default 0).\n        You wait for these.\n    background_touches: how many of the best remaining candidates to confirm AFTER the\n        answer is sent (0-10, default 0). You do NOT wait for these and they are NOT\n        confirmed in this response — `background.queued` says how many are running. Ask\n        again in a moment and they come back already confirmed, at no extra cost, because\n        they land in the same cache. Use this to keep a long list moving without making\n        the user wait: \"here are 25 people, the best 8 are being confirmed now\".\n    want_notes: draft a connection note per confirmed candidate. Default false.","operationId":"find_prospects","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindProspectsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/search_posts_by_topic":{"post":{"tags":["mcp"],"summary":"Search Posts By Topic","description":"Search LinkedIn posts by topic and return each post with its author as a candidate.\nReads only, drafts nothing, and opens no profiles: the post itself is the evidence and\nthe author comes back inline, so the cost is the same whether it returns 1 post or 50.\n\nUSE THIS when the person is defined by what they posted: \"who is posting about X\",\n\"engineering managers at Nvidia who posted about hiring this month\", \"people in my\nnetwork talking about Y\". One call answers the whole question — do not search people\nand then read each one's posts.\n\nDO NOT USE for a people search with no topic (search_prospects / find_prospects), or when\nthe user wants messages written (discover_prospects). In particular, this tool answers\n\"who POSTED about hiring\" — NOT \"who HAS the #hiring badge on their profile\", which is\nfind_prospects' `hiring` flag and costs a profile touch per person confirmed. The two\nreturn different people.\n\nArgs:\n    keywords: POST TEXT — words the post body must contain, e.g. \"hiring\", \"open roles\",\n        \"RAG evals\". \"#hiring\" here matches the hashtag someone TYPED IN A POST; it is not\n        the profile badge and implies nothing about it. Free: one post search, no profile\n        opened. (The badge is find_prospects' `hiring`, and costs a touch per person.)\n    author_company: Company the author currently works at, by name, e.g. \"Nvidia\".\n    author_keywords: Text in the author's headline or title, e.g. \"engineering manager\".\n    date_posted: past_day | past_week | past_month (default past_month). Newest first.\n    posted_by: first_connections or people_you_follow to stay inside the user's network.\n    limit: Max posts to return (1-50, default 10). Reposts are dropped.\n\nReturns {\"posts\": [...], \"count\": N, \"has_more\": bool}. Each post: text, date, post_url,\nreaction_count, comment_count, and author {name, headline, is_company, handle}. The\nhandle is a prof_ handle (null for company pages) — pass it to get_linkedin_profile for\nthe full profile, or straight to schedule_dm_to_person / enroll_prospect.","operationId":"search_posts_by_topic","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchPostsByTopicRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/discover_prospects":{"post":{"tags":["mcp"],"summary":"Discover Prospects","description":"Instantly search for people, read their profiles, and generate connection note\ndrafts, all in about 10 to 15 seconds.\nReturns draft handles ready to approve or skip.\nComplement to run_outreach_pipeline, which works from the user's saved targets.\n\nData accuracy: profile names, companies, and headlines are fetched live from\nLinkedIn and reflect real, current information. Notes reference actual post\ncontent when available; when no posts are found, notes are conservative and\nbased only on verifiable headline/company data.\n\nsuggested_send_at is a suggestion only. Nothing sends until approve_draft is\nexplicitly called. Drafts remain in pending_review indefinitely.\n\nEach draft includes a `profile` field with: about, current_role, past_companies,\neducation, certifications, skills, follower_count, recent_posts. Display this\nalongside the note so the user understands who was found.\n\nArgs:\n    query: Search query e.g. \"ML engineer at Series B startup\"\n    location: Location filter e.g. \"San Francisco Bay Area\"\n    limit: Max prospects to process (1-10, default 5)\n    hiring_only: the profile BADGE (is_hiring), not post text. This tool opens every\n        candidate's full profile anyway, so the badge is genuinely CONFIRMED here rather\n        than guessed — the filter is applied after that fetch and costs nothing extra.\n        Use it for \"find me hiring managers to connect with\". Default false (no filter).\n        For \"who POSTED about hiring\", use search_posts_by_topic: different question,\n        different people, and free.\n    open_to_work_only: same, for the is_open_to_work BADGE. If both this and hiring_only\n        are true, a candidate matching either badge is kept.","operationId":"discover_prospects","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverProspectsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/get_linkedin_profile":{"post":{"tags":["mcp"],"summary":"Get Linkedin Profile","description":"Look up LinkedIn profile(s) for a person, or several known people at once.\n\nAt least one of name, linkedin_id, iridium_handle, linkedin_ids, or iridium_handles\nmust be provided. company, education, location, headline_keywords are optional hints\nthat narrow a name-based search.\n\nCosts one profile touch per person against the daily budget — this is the expensive way\nto look someone up, and the right one when you already know WHO you want. To find people\nyou cannot yet name, use find_prospects.\n\nReturns {\"results\": [...], \"total\": N, \"query_used\": str | None}.\nResults are fully read: about, current_role, past_companies, education,\ncertifications, skills, recent_posts, prof_handle, and the two profile BADGES as\n`is_hiring` / `is_open_to_work`. Opening the profile is the only thing that reveals those\nbadges, and this call already opened it, so they cost nothing extra here — this is the\ncheapest confirmation available for a person you can already name. (`find_prospects` with\nmax_touches does the same for people you cannot yet name, one touch each.)","operationId":"get_linkedin_profile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetLinkedInProfileRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/create_sequence":{"post":{"tags":["mcp"],"summary":"Create Sequence","description":"CURRENTLY DISABLED. This tool returns 501 and creates nothing.\n\nMulti-step sequences are switched off while the per-recipient safety checks are rebuilt.\nDo not call this. To do multi-step follow-up today, approve one message now and schedule\nthe next with schedule_manual_dm or schedule_chat_reply at a later send_at.","operationId":"create_sequence","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSequenceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/enroll_prospect":{"post":{"tags":["mcp"],"summary":"Enroll Prospect","description":"CURRENTLY DISABLED. This tool returns 501 and enrolls nobody.\n\nSee create_sequence. Schedule follow-ups explicitly instead.","operationId":"enroll_prospect","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollProspectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_drafts":{"get":{"tags":["mcp"],"summary":"List Drafts","description":"Connection-note drafts waiting for review. Nothing here has been sent.\n\nEach entry has a draft_ handle, who it is addressed to, a preview of the note, and the\ntime Iridium suggests sending it. Show the notes to the user and let them decide.\nApproving is a separate step: call approve_draft only when the user explicitly asks.","operationId":"list_drafts","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/approve_draft":{"post":{"tags":["mcp"],"summary":"Approve Draft","description":"Approve one connection-note draft and schedule it. THIS SENDS.\n\nCall it only when the user has explicitly asked for this specific draft to go out.\nFinding or drafting something is not permission to approve it. The connection request\ncounts against the user's daily limit.\n\nReturns a draft_ handle for the scheduled send, usable with reschedule_send and\ncancel_scheduled, so there is no need to call list_scheduled afterwards.\n\nArgs:\n    handle: draft_ handle from list_drafts, discover_prospects, or run_outreach_pipeline.\n    override_text: replacement note text. Omit to send the drafted text as written, or\n                    pass \"\" to send the connection request with no note at all.\n    send_at: \"now\" to send immediately, or ISO-8601 with an offset, for example\n             \"2026-06-11T09:00:00+00:00\". Omit and Iridium picks a sensible time.","operationId":"approve_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/skip_draft":{"post":{"tags":["mcp"],"summary":"Skip Draft","description":"Discard a connection-note draft without sending it. Nothing goes to LinkedIn.\n\nArgs:\n    handle: draft_ handle from list_drafts.","operationId":"skip_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/rewrite_draft":{"post":{"tags":["mcp"],"summary":"Rewrite Draft","description":"Rewrite a connection-note draft with an instruction. Does not send.\n\nThe rewrite runs in the background, so the new text is not in the response. Call\nget_draft_detail or list_drafts afterwards to read the result.\n\nArgs:\n    handle: draft_ handle from list_drafts.\n    instruction: what to change, for example \"shorter\", \"lead with the mutual connection\",\n                 \"drop the compliment\".","operationId":"rewrite_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/get_draft_detail":{"post":{"tags":["mcp"],"summary":"Get Draft Detail","description":"Everything Iridium knows about one connection-note draft: the full note text, the angle\nit was written from, and the profile it is addressed to. Read-only.\n\nUse this when the user asks why a draft says what it says, or wants the full text that\nlist_drafts only previews.\n\nArgs:\n    handle: draft_ handle from list_drafts.","operationId":"get_draft_detail","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetDraftDetailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_scheduled":{"get":{"tags":["mcp"],"summary":"List Scheduled","description":"Everything approved and queued to send, with the time each one goes out.\n\nThese have been approved and WILL send unless cancelled. Use reschedule_send to move one\nand cancel_scheduled to stop it.","operationId":"list_scheduled","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/cancel_scheduled":{"post":{"tags":["mcp"],"summary":"Cancel Scheduled","description":"Cancel a scheduled send so it never goes out. Nothing reaches LinkedIn.\n\nArgs:\n    handle: draft_ handle from list_scheduled or approve_draft.","operationId":"cancel_scheduled","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelScheduledRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/reschedule_send":{"post":{"tags":["mcp"],"summary":"Reschedule Send","description":"Move a pending send to a different time. It has already been approved, so it will send.\n\nArgs:\n    handle: draft_ handle from list_scheduled or approve_draft\n    send_at: \"now\" to send immediately, else ISO-8601 e.g. \"2026-06-11T09:00:00+00:00\"","operationId":"reschedule_send","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/schedule_manual_dm":{"post":{"tags":["mcp"],"summary":"Schedule Manual Dm","description":"Schedule a direct message to someone you're already connected with.\nUse list_chats to find the chat_handle for a conversation.\n\nArgs:\n    chat_handle: chat_ handle from list_chats or list_replies\n    text: Message text to send\n    send_at: \"now\" to send immediately, else ISO-8601 e.g. \"2026-06-11T09:00:00+00:00\"\n    message_type: \"direct_message\" (default) or \"inmail\"","operationId":"schedule_manual_dm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleManualDMRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/search_chats":{"post":{"tags":["mcp"],"summary":"Search Chats","description":"Search your LinkedIn inbox by message content and by structured filters, all local\n(no LinkedIn quota). Free text matches participant names and message bodies (e.g.\n\"who mentioned CMU\"). Operators can be passed in `q` or as discrete args:\n  is:unread/read · from:<name> · company:<name> · after:/before:<YYYY-MM-DD> ·\n  newer:<N>d · older:<N>d\nReturns chat handles ready for get_thread / schedule_chat_reply.","operationId":"search_chats","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchChatsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/find_stale_threads":{"post":{"tags":["mcp"],"summary":"Find Stale Threads","description":"Surface dropped balls, meaning threads with no activity in `days`:\n  ghosted    → you sent the last message and got no reply\n  owed_reply → they replied and you haven't responded\nThreads with a pending scheduled reply are excluded. Oldest first, except threads whose\ncontact shows a cached #hiring or #OpenToWork profile BADGE sort ahead of ones that don't.\n\nhiring_signal: true when this contact's cached LinkedIn profile shows the #hiring or\n#OpenToWork profile BADGE (not post text) — best-effort, from a prior profile fetch, never a new one. False\nmeans either confirmed absent or never checked; treat it as \"nothing to report,\" not\na confirmed negative.","operationId":"find_stale_threads","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindStaleThreadsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/summarize_thread":{"post":{"tags":["mcp"],"summary":"Summarize Thread","description":"Structured summary of one conversation so you don't re-read it: participants, company,\nthe core ask, current state, and the suggested next step. Cached until a new message\narrives (pass force=true to recompute).\n\nArgs:\n    chat_handle: chat_ handle from list_chats / search_chats\n    force: recompute even if a fresh cached summary exists","operationId":"summarize_thread","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SummarizeThreadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/classify_inbox":{"post":{"tags":["mcp"],"summary":"Classify Inbox","description":"Triage the inbox into buckets (default: recruiter_inbound, candidate_reply,\ncold_outreach_reply, spam) with a confidence per thread. Cache-first; repeat it to finish\na large backlog (`remaining`).\n\nEach thread is judged on its type (regular DM / inmail / sponsored / linkedin_offer), the\nsender's name and headline, the subject line if it has one, and the latest message.\nlinkedin_offer threads are labelled spam with no model call (`auto_labeled`).\n\nArgs:\n    taxonomy: optional custom list of labels to bucket into\n    limit: max PROVIDER CALLS to spend this call, retries included (default 25). Keep it\n        near 20 — the calls are sequential and a larger batch can outrun the client\n        timeout. `failed` counts threads the model would not label.","operationId":"classify_inbox","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassifyInboxRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/schedule_dm_to_person":{"post":{"tags":["mcp"],"summary":"Schedule Dm To Person","description":"Send or schedule a direct message to a person named in plain English. THIS SENDS.\n\nResolves the name itself: it checks existing conversations, then saved profiles, then\nLinkedIn search. The response carries a `confidence` field. When it is low, confirm the\nmatch with the user before the message goes out rather than trusting the resolution.\n\nUSE THIS when the user names someone and no chat_ handle is at hand.\nDO NOT USE when you already have a chat_ handle. Use schedule_manual_dm instead.\n\nArgs:\n    name: the person's name as the user said it.\n    text: the exact message to send.\n    send_at: \"now\" to send immediately, or ISO-8601 with an offset.\n    profile_handle: prof_ handle, to skip resolution when it is already known.","operationId":"schedule_dm_to_person","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleDmToPersonRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_replies":{"get":{"tags":["mcp"],"summary":"List Replies","description":"Inbound threads awaiting a reply (they messaged last, not yet actioned). Returns chat\nhandles ready for draft_reply / get_thread / schedule_chat_reply.\n\nhiring_signal: true when this sender's cached LinkedIn profile shows the #hiring or\n#OpenToWork profile BADGE (not post text) — best-effort, from a prior profile fetch, never a new one. False\nmeans either confirmed absent or never checked; treat it as \"nothing to report,\" not\na confirmed negative.","operationId":"list_replies","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/backfill_chats":{"post":{"tags":["mcp"],"summary":"Backfill Chats","description":"Pull all existing LinkedIn conversations from Unipile into Iridium — every thread in one\npass, not a page at a time. Run this once after connecting LinkedIn to populate your\ninbox; re-running is safe and repairs older rows in place.\n\nRecords each thread's kind (regular DM / inmail / sponsored / linkedin_offer), its subject\nline where it has one, and a real message preview. Re-run it once after an upgrade to\nrepair rows that predate those fields.\nTakes 10-30 seconds depending on conversation count.","operationId":"backfill_chats","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_chats":{"get":{"tags":["mcp"],"summary":"List Chats","description":"The user's LinkedIn conversations, most recent first. Reads from Iridium's synced copy,\nso it uses no LinkedIn quota.\n\nReturns chat_ handles for get_thread, summarize_thread, draft_reply, and\nschedule_manual_dm. If the inbox looks empty on a new account, run backfill_chats once.","operationId":"list_chats","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/get_thread":{"post":{"tags":["mcp"],"summary":"Get Thread","description":"Read a full LinkedIn conversation with one person, local-first, using no Unipile quota\nonce synced. Returns the most recent messages oldest-first; pass `before` (the\nreturned next_cursor) to scroll up into older history.\n\nArgs:\n    chat_handle: chat_ handle from list_chats / search_chats\n    before: next_cursor from a prior call, to load older messages\n    limit: messages per page (default 10, max 50)","operationId":"get_thread","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetThreadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/schedule_chat_reply":{"post":{"tags":["mcp"],"summary":"Schedule Chat Reply","description":"Send or schedule a message into an existing conversation. THIS SENDS.\n\nIt goes into a thread that already exists, so it creates no new connection and does not\ncount against the daily connection limit. Call it only when the user has approved this\nspecific message.\n\nArgs:\n    chat_handle: chat_ handle from list_chats or search_chats.\n    text: the exact message to send.\n    send_at: \"now\" to send immediately, or ISO-8601 with an offset. Omit for immediate.","operationId":"schedule_chat_reply","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleChatReplyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/draft_reply":{"post":{"tags":["mcp"],"summary":"Draft Reply","description":"Generate a reply to a conversation, using the full thread + your voice, for review.\nCreates a pending reply draft, one per thread. Approve, rewrite, or skip it next.\n\nArgs:\n    chat_handle: chat_ handle from list_chats / search_chats / list_replies\n    intent: optional steer for the reply (a goal or tone)","operationId":"draft_reply","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftReplyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_reply_drafts":{"get":{"tags":["mcp"],"summary":"List Reply Drafts","description":"Pending inbound-reply drafts awaiting your review.","operationId":"list_reply_drafts","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/approve_reply_draft":{"post":{"tags":["mcp"],"summary":"Approve Reply Draft","description":"Approve a reply draft and schedule it into the existing thread. THIS SENDS. It adds no\nnew connection and does not count against the daily limit.","operationId":"approve_reply_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveReplyDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/rewrite_reply_draft":{"post":{"tags":["mcp"],"summary":"Rewrite Reply Draft","description":"Rewrite a reply draft with an instruction, for example 'shorter' or 'propose Tuesday'.\nDoes not send.","operationId":"rewrite_reply_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteReplyDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/skip_reply_draft":{"post":{"tags":["mcp"],"summary":"Skip Reply Draft","description":"Discard a reply draft without sending.","operationId":"skip_reply_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipReplyDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/get_user_profile":{"get":{"tags":["mcp"],"summary":"Get User Profile","description":"The user's Iridium account state: their saved targeting and voice settings, daily limits\nand how much of each is used today, counts of pending drafts and queued sends, unread\nreplies, and whether LinkedIn is connected. Read-only.\n\nCheck this before any bulk action to see what capacity is left, and before asking the user\nto supply a setting — it may already be set. The setting fields are named exactly as\nupdate_settings takes them, so anything read here can be written straight back.","operationId":"get_user_profile","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/update_settings":{"post":{"tags":["mcp"],"summary":"Update Settings","description":"Change the user's profile, targeting and limits. Persists immediately, sends nothing.\n\nEvery field is optional and only the ones passed are changed; each field's own description\nsays what it does. Pipelines read these settings, so this is how \"only comment on AI posts\"\nor \"cap me at 5 a day\" gets applied.\n\nThis is also how you clear a blocked pipeline. When a run fails for a missing setting, the\nerror names the field: ask the user for it in plain language, send it here, then retry the\nrun. Never invent a value the user did not give you.","operationId":"update_settings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/run_outreach_pipeline":{"post":{"tags":["mcp"],"summary":"Run Outreach Pipeline","description":"Draft 3 connection notes against the user's saved targets and return them inline in\nabout 5 seconds. Produces drafts only and sends nothing.\n\nAfter calling this tool, ALWAYS display every draft to the user in full:\n\n  **[Name], [Headline] at [Company]**\n  [note_text, displayed in full]\n  Suggested send: [suggested_send_at]\n\nDo NOT call approve_draft or schedule anything unless the user explicitly asks.","operationId":"run_outreach_pipeline","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/run_commenting_pipeline":{"post":{"tags":["mcp"],"summary":"Run Commenting Pipeline","description":"Generate up to 3 LinkedIn comment drafts on relevant posts, found by searching all of\nLinkedIn for the user's saved comment_topics.\n\nUSE THIS when the user wants to comment by TOPIC, or names no target in particular:\n  \"find me some posts to comment on\"\n  \"comment on posts about RAG\"\n  \"who's worth engaging with this week?\"\n\nDO NOT USE when the user names specific companies (\"comment on posts from people at\nStripe\"). Use run_company_commenting_pipeline instead.\n\nAfter calling this tool, ALWAYS display every draft to the user in full:\n\n  **Post by [author_name]:**\n  [post_text, the complete text, never truncated]\n\n  **Drafted comment:**\n  [comment_text, displayed in full]\n\nDo NOT call approve_comment_draft or schedule anything unless the user explicitly asks.","operationId":"run_commenting_pipeline","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/run_company_commenting_pipeline":{"post":{"tags":["mcp"],"summary":"Run Company Commenting Pipeline","description":"Generate LinkedIn comment drafts on recent posts by people who work at specific companies.\n\nUSE THIS when the user names one or more companies:\n  \"comment on posts from people at Stripe\"\n  \"I want to be visible to Anthropic and OpenAI engineers\"\n  \"engage with anything Ramp employees posted this week\"\n\nDO NOT USE for topic- or keyword-based commenting (\"find me AI posts to comment on\",\n\"comment on posts about RAG\"). Use run_commenting_pipeline instead, which searches the\nwhole of LinkedIn by the user's saved comment_topics.\n\nHow it differs: this tool searches by AUTHOR COMPANY and recency only, with no keyword\nfilter, because ANDing a keyword against a single employer usually returns nothing.\nTopical relevance is then judged by the model over whatever those employees actually\nposted. As a result it may legitimately return zero drafts if nobody at those companies\nposted anything worth engaging with; that is a valid outcome, not an error, and you\nshould say so plainly rather than retrying.\n\nTakes up to 5 companies per call. Companies with no matching LinkedIn page come back in\nunresolved_companies, so tell the user which ones failed.\n\nAfter calling this tool, ALWAYS display every draft to the user in full:\n\n  **Post by [author_name] ([company]):**\n  [post_text, the complete text, never truncated]\n\n  **Drafted comment:**\n  [comment_text, displayed in full]\n\nDo NOT call approve_comment_draft or schedule anything unless the user explicitly asks.\n\nArgs:\n    companies: Company names, e.g. [\"Stripe\", \"Ramp\"]. Max 5.\n    n: How many drafts to generate (default 3, capped by the user's remaining daily cap).\n    date_posted: \"past_24h\" | \"past_week\" | \"past_month\". Default \"past_week\".","operationId":"run_company_commenting_pipeline","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyCommentRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/run_content_pipeline":{"post":{"tags":["mcp"],"summary":"Run Content Pipeline","description":"Draft LinkedIn posts for the user to publish, from their saved content topics and voice.\nProduces drafts only and publishes nothing.\n\nRuns in the background and returns immediately. Call list_content_drafts to read the\nresults. Requires content topics to be set, otherwise it returns an error saying so.\nRuns at most once a day, and returns status \"skipped\" with reason \"already ran today\"\non a second call.","operationId":"run_content_pipeline","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_comment_drafts":{"get":{"tags":["mcp"],"summary":"List Comment Drafts","description":"List pending comment drafts awaiting review.\n\nALWAYS display every draft to the user in full:\n\n  **Post by [author_name]:**\n  [post_text, the complete text, never truncated]\n\n  **Drafted comment:**\n  [comment_text, displayed in full]\n\nDo NOT call approve_comment_draft or schedule anything unless the user explicitly asks.","operationId":"list_comment_drafts","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/approve_comment_draft":{"post":{"tags":["mcp"],"summary":"Approve Comment Draft","description":"Approve one comment draft and schedule it to post. THIS PUBLISHES A PUBLIC COMMENT.\n\nThe comment appears on someone else's post under the user's name, so call this only when\nthe user has explicitly approved this specific comment. It counts against their daily\ncomment limit.\n\nArgs:\n    handle: cdft_ handle from list_comment_drafts.\n    comment_text: replacement text. Omit to post the drafted text as written.\n    comment_at: \"now\" to post immediately, or ISO-8601 with an offset. Omit to let\n                Iridium pick a time.","operationId":"approve_comment_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveCommentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/skip_comment_draft":{"post":{"tags":["mcp"],"summary":"Skip Comment Draft","description":"Discard a comment draft without posting it. Nothing reaches LinkedIn.\n\nArgs:\n    handle: cdft_ handle from list_comment_drafts.","operationId":"skip_comment_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipCommentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/rewrite_comment_draft":{"post":{"tags":["mcp"],"summary":"Rewrite Comment Draft","description":"Rewrite a comment draft with an instruction. Does not post.\n\nThe rewrite runs in the background, so call list_comment_drafts afterwards to read the\nnew text.\n\nArgs:\n    handle: cdft_ handle from list_comment_drafts.\n    instruction: what to change, for example \"shorter\", \"ask a question at the end\",\n                 \"drop the praise\". Omit for a general retry.","operationId":"rewrite_comment_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteCommentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/edit_comment_draft":{"post":{"tags":["mcp"],"summary":"Edit Comment Draft","description":"Replace a comment draft's text with text the user wrote. Saves only, does not post.\n\nUse this when the user dictates their own wording. Approving is still a separate step.\n\nArgs:\n    handle: cdft_ handle from list_comment_drafts.\n    comment_text: the exact text to store on the draft.","operationId":"edit_comment_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditCommentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_content_drafts":{"get":{"tags":["mcp"],"summary":"List Content Drafts","description":"The user's own LinkedIn post drafts, waiting for review. Nothing here has been published.\n\nThese are posts the user would publish under their own name, distinct from comment drafts\n(which go on other people's posts) and outreach drafts (which are connection notes).\nShow each draft in full and let the user decide. Publishing is a separate step.","operationId":"list_content_drafts","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/approve_content_draft":{"post":{"tags":["mcp"],"summary":"Approve Content Draft","description":"Publish or schedule one of the user's own LinkedIn posts. THIS POSTS PUBLICLY.\n\nThe post appears on the user's profile. Call this only when they have explicitly approved\nthis specific post.\n\nArgs:\n    handle: ctnt_ handle from list_content_drafts.\n    post_text: the final text to publish. Required, because the user is expected to edit\n               before publishing.\n    scheduled_at: ISO-8601 with an offset to schedule it. Omit to publish immediately.","operationId":"approve_content_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveContentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/skip_content_draft":{"post":{"tags":["mcp"],"summary":"Skip Content Draft","description":"Discard a post draft without publishing it. Nothing reaches LinkedIn.\n\nArgs:\n    handle: ctnt_ handle from list_content_drafts.","operationId":"skip_content_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipContentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/rewrite_content_draft":{"post":{"tags":["mcp"],"summary":"Rewrite Content Draft","description":"Rewrite a post draft with an instruction. Does not publish.\n\nThe rewrite runs in the background, so call list_content_drafts afterwards to read the\nnew text.\n\nArgs:\n    handle: ctnt_ handle from list_content_drafts.\n    instruction: what to change, for example \"punchier hook\", \"cut it in half\",\n                 \"end on a question\".","operationId":"rewrite_content_draft","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteContentDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/connect_linkedin_credentials":{"post":{"tags":["mcp"],"summary":"Connect Linkedin Credentials","description":"Connect LinkedIn via username and password.\nCreates a fresh Unipile-managed session. It does NOT touch your existing browser\nsession, so your LinkedIn tab stays logged in.\n\nIf LinkedIn requires 2FA, returns checkpoint=True with an account_id.\nFollow up with connect_linkedin_checkpoint to complete the flow.","operationId":"connect_linkedin_credentials","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkedInCredentialsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/connect_linkedin_checkpoint":{"post":{"tags":["mcp"],"summary":"Connect Linkedin Checkpoint Mcp","description":"Submit a LinkedIn 2FA verification code to complete the connection.\nUse this after connect_linkedin_credentials returns checkpoint=True.\n\nArgs:\n    account_id: The account_id returned by connect_linkedin_credentials\n    code: The verification code LinkedIn sent to your email or phone","operationId":"connect_linkedin_checkpoint_mcp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkedInCheckpointRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/connect_linkedin_url":{"get":{"tags":["mcp"],"summary":"Connect Linkedin Url","description":"Get a Unipile hosted-auth URL to connect LinkedIn.\nThe user opens this URL in their browser to complete the OAuth flow. No password or\ncookie is needed. Connection is confirmed automatically\nvia webhook once the user completes the flow.","operationId":"connect_linkedin_url","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/connect_linkedin_li_at":{"post":{"tags":["mcp"],"summary":"Connect Linkedin Li At","description":"Connect LinkedIn by pasting your li_at session cookie.\nLower friction than OAuth, with no redirect needed.\n\nTo get your li_at cookie:\n  1. Log in to linkedin.com in Chrome\n  2. Open DevTools → Application → Cookies → linkedin.com\n  3. Copy the value of the 'li_at' cookie","operationId":"connect_linkedin_li_at","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkedInLiAtRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/disconnect_linkedin":{"post":{"tags":["mcp"],"summary":"Disconnect Linkedin","description":"Disconnect your LinkedIn account from Iridium.\nRemoves the connection from Unipile and clears it from your profile.\nEverything that needs LinkedIn stops until it is reconnected.","operationId":"disconnect_linkedin","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/get_activity_log":{"get":{"tags":["mcp"],"summary":"Get Activity Log","description":"Read the user's activity log. Every action Iridium has taken, newest first.\n\nIridium records every attempted action in an append-only ledger: what ran, when, whether it\nsucceeded, and the text that was actually sent. This tool reads it. Nothing is sent or\nchanged by calling it.\n\nUSE THIS when the user asks what happened, in any form:\n  \"what did you do today?\"\n  \"did that comment actually post?\"\n  \"show me everything from yesterday\"\n  \"what failed?\"\n\nAnswer from this log rather than from memory of earlier turns in the conversation. The log\nis the record; the conversation is not.\n\nEach entry has: at, action (readable label), surface, status, target (who or what it\ntouched), summary (one sentence), detail (the text that was sent), and error when it failed.\nEntries older than the user's data-retention window may have no target name, because the\nprofile behind it has been erased. That is expected and is not a failure.\n\nArgs:\n    limit: Entries per page, 1-100 (default 20).\n    cursor: next_cursor from a previous call, to fetch the following page.\n    surface: \"linkedin\" for actions taken on LinkedIn, \"iridium\" for actions inside\n             Iridium such as sign-ins and draft decisions. Omit for both.\n    status: \"success\", \"failed\", or \"skipped\". Omit for all.\n    action_type: exact slug filter, e.g. \"comment_send\". Omit for all.","operationId":"get_activity_log","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"surface","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"action_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/mcp/tools/get_last_action":{"get":{"tags":["mcp"],"summary":"Get Last Action","description":"Read back the single most recent action Iridium took, with its full text.\n\nUse this to confirm something just happened, after approving or scheduling anything, or\nwhen the user asks \"did that work?\". Cheaper than get_activity_log when only the last\naction matters. Nothing is sent or changed by calling it.\n\nReturns {\"item\": null} if the account has no recorded actions yet.","operationId":"get_last_action","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/report_issue":{"post":{"tags":["mcp"],"summary":"Report Issue","description":"Send the Iridium team an anonymous note about something that broke while you were working.\n\nReturns {\"received\": true}. Nothing is sent to LinkedIn and nothing about the account is\nchanged. This is NOT a support ticket: the report carries nothing that identifies the user,\nnobody will reply, and there is no way to follow it up. It exists so the team can see which\nworkflows break most and which ones users cannot work without.\n\nUSE THIS when an Iridium tool failed, returned something wrong, or made a workflow the user\ncares about impossible to finish — especially if you have hit it more than once.\n\nDO NOT USE when the user just needs to be told something (a cap was reached, LinkedIn is\ndisconnected, a draft was skipped) — tell them, that is not a break. Do not use it to ask a\nquestion, request a feature, or chase a previous report. Call list_solved_issues first; if\nthe problem is already fixed there, tell the user instead of reporting it again.\n\nArgs:\n    what_broke: One line on what failed. Max 200 characters.\n    how_it_happened: One line on the steps that led to it. Max 200 characters.\n    frequency: One of first_time, sometimes, every_time.\n    importance: One of nice_to_have, important, blocking — how much the user needs this\n                workflow to work.\n\nNEVER include personal or identifying information in either text field: no names, company\nnames, job titles, message or post text, email addresses, phone numbers, URLs, @handles, or\nIridium handles (draft_, chat_, prof_). Describe the shape of the failure, not the data it\nhappened to. \"Approving a connection note fails when the note ends in a question mark\" is a\nreport; anything naming a person is not. Text containing an identifier is rejected with 422 —\nrestate it without one rather than working around the check.","operationId":"report_issue","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportIssueRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/list_solved_issues":{"get":{"tags":["mcp"],"summary":"List Solved Issues","description":"Known problems the Iridium team has already fixed, newest first.\n\nReturns a list of {title, resolution, solved_at}. Read-only — nothing is sent or changed.\n\nUSE THIS before report_issue, to check whether the thing you just hit is already solved, and\nwhen a user asks whether a problem they remember has been fixed.\n\nDO NOT USE as general help or documentation: this is only the list of past breakages, not a\ndescription of what Iridium can do. Call list_tools for that.","operationId":"list_solved_issues","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/mcp/tools/delete_account":{"post":{"tags":["mcp"],"summary":"Delete Account","description":"Delete your Iridium account.\nDeactivates your account immediately, destroys the LinkedIn session at Unipile, and\ncancels your subscription. All scheduled work stops at once.\nYour personal data (profiles, drafts, chats, settings) is retained for 30 days and then\npermanently erased; your action history is retained for audit.","operationId":"delete_account","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/.well-known/oauth-authorization-server":{"get":{"tags":["oauth"],"summary":"Oauth Metadata","operationId":"oauth_metadata__well_known_oauth_authorization_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["oauth"],"summary":"Protected Resource Metadata","operationId":"protected_resource_metadata__well_known_oauth_protected_resource_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["oauth"],"summary":"Protected Resource Metadata Mcp","operationId":"protected_resource_metadata_mcp__well_known_oauth_protected_resource_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/register":{"post":{"tags":["oauth"],"summary":"Register","description":"Minimal RFC 7591 Dynamic Client Registration.\n\nWe are a public client using PKCE, so there is no client secret to issue — we accept\nany registration and return a generated client_id. Claude/Codex/opencode call this\nbefore /oauth/authorize because they hold no pre-registered client for this server.","operationId":"register_oauth_register_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/authorize":{"get":{"tags":["oauth"],"summary":"Authorize Form","operationId":"authorize_form_oauth_authorize_get","parameters":[{"name":"response_type","in":"query","required":true,"schema":{"type":"string","title":"Response Type"}},{"name":"client_id","in":"query","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","title":"Redirect Uri"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string","title":"Code Challenge"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"type":"string","default":"S256","title":"Code Challenge Method"}},{"name":"resource","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["oauth"],"summary":"Authorize Submit","operationId":"authorize_submit_oauth_authorize_post","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_authorize_submit_oauth_authorize_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/approve":{"post":{"tags":["oauth"],"summary":"Authorize Approve","description":"One-click authorization from the SPA consent screen.\n\nThe user is already authenticated with their normal Iridium access token, so no API\nkey is entered anywhere. We mint an auth code bound to this user and return the URL\nthe SPA redirects the browser to (back to the MCP client's redirect_uri). This is the\nsession-authenticated counterpart to authorize_submit, which still exists as an\nAPI-key fallback for browsers with no Iridium session.","operationId":"authorize_approve_oauth_approve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/routers__api__oauth__ApproveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/oauth/disconnect":{"post":{"tags":["oauth"],"summary":"Disconnect","description":"Revoke every connector token this user has authorized.\n\nThe tokens are 30-day JWTs with no server-side row to delete, so revocation is an epoch:\nanything minted before now is refused by lifecycle.enforce_token_live. Clearing the\nconnected-agent state alongside it keeps the settings screen honest.\n\nNot scoped to one connector on purpose — a token carries no client identity to scope by\n(/oauth/register persists nothing), so pretending otherwise would be a lie in the UI.","operationId":"disconnect_oauth_disconnect_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/oauth/token":{"post":{"tags":["oauth"],"summary":"Token","operationId":"token_oauth_token_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_token_oauth_token_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login":{"post":{"tags":["auth"],"summary":"Login","operationId":"login_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh","operationId":"refresh_auth_refresh_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_auth_logout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/social-login":{"post":{"tags":["auth"],"summary":"Social Login","operationId":"social_login_auth_social_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SocialLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/demo":{"post":{"tags":["auth"],"summary":"Demo Login","description":"Issue a throwaway demo account — the only unauthenticated route that creates a user.\n\nA fresh row per click, not one shared account: the chat transcript is per-user, so a\nshared demo would show every visitor everyone else's conversation.\n\nConsent is NOT recorded here, for the same reason it isn't in social_login above —\nclicking \"try the demo\" is not agreeing. The demo user lands on the same blocking Terms\nscreen as everyone else. What they skip is onboarding, which asks for a LinkedIn they\nare deliberately not connecting.\n\nThe address is a reserved .invalid domain (RFC 2606): unique, and provably unable to\nreceive the mail this account must never be sent.","operationId":"demo_login_auth_demo_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}}}}},"/auth/reactivate":{"post":{"tags":["auth"],"summary":"Reactivate","description":"Restore a soft-deleted account. The explicit act that signing in no longer performs.\n\nReachable while inactive: /auth/ is exempt from the deleted_gate middleware, and this route\nuses the allow-inactive dependency.\n\nIssues a FRESH token pair — delete_user() revoked every refresh family, so the caller's old\nrefresh token is dead even though its access token still decodes.","operationId":"reactivate_auth_reactivate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/admin/users":{"post":{"tags":["admin"],"summary":"Create User","operationId":"create_user_admin_users_post","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/activate":{"post":{"tags":["admin"],"summary":"Activate User","operationId":"activate_user_admin_users__user_id__activate_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}":{"delete":{"tags":["admin"],"summary":"Hard Delete User","description":"Permanently remove a user and everything that hangs off them. No grace period.\n\nDeliberately NOT shared_account.delete_user: that soft-deletes into the 30-day reactivate\nwindow, which is right for a person leaving and wrong for both callers here — the smoke\nsuite tearing down its own fixture, and pruning seeded or junk accounts off the roster.\n\nA row-level DELETE lets the ondelete=\"CASCADE\" foreign keys do the work, so this also\nremoves the user's audit rows and consent record. That is the trade: PP §8 says audit logs\nsurvive deletion and _PURGE_MODELS preserves them, so reach for this only when there is no\nreal user relationship to document — a test fixture, a duplicate, a fake address. Use\ndelete_user for an actual person.\n\nOne admin_hard_delete row with user_id=NULL survives as the record, since anything keyed to\nthe user cascades away with them.","operationId":"hard_delete_user_admin_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"Delete even if a Stripe subscription exists","default":false,"title":"Force"},"description":"Delete even if a Stripe subscription exists"},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/pipeline/run-all":{"post":{"tags":["admin"],"summary":"Admin Run All Pipelines","description":"DISABLED — fan-out discovery across every account is intentionally switched off.\n\nUnattended people discovery must not run inside users' accounts. Pipelines are now\nuser-initiated only: POST /pipeline/run (web) or the run_outreach_pipeline MCP tool.\nThe body is kept, commented, so the behaviour can be restored deliberately rather than\nreconstructed. Re-enabling also requires restoring CRON_LINE in deploy/setup_hetzner.sh,\nand now a fan-out would drive run_people_pipeline_fast — the background pipeline this\ncalled is deleted.","operationId":"admin_run_all_pipelines_admin_pipeline_run_all_post","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/test-email":{"post":{"tags":["admin"],"summary":"Test Email","description":"Send one message and report Resend's verbatim answer. A probe, not a business action.\n\nExists because diagnosing mail used to mean reading container logs and guessing: the\ntransport reported a bare bool, and the reason Resend gave was discarded. Now any mail\nproblem is one call — is the key valid, is the domain verified, was the address refused.","operationId":"test_email_admin_test_email_post","parameters":[{"name":"to","in":"query","required":true,"schema":{"type":"string","description":"Recipient for the probe","title":"To"},"description":"Recipient for the probe"},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tos-notice":{"post":{"tags":["admin"],"summary":"Send Tos Notice","description":"Email every active user that the Terms have changed. Run once per release.\n\nThis is the notice half of notice-based consent: when CURRENT_TOS_REQUIRES_REACCEPT is\nFalse, nobody is blocked, so this email is the ONLY thing standing between a version bump\nand terms a user never heard about. Send it on the day you deploy the new text.\n\nIdempotent per version. \"Have we already told this user about THIS version?\" is answered\nfrom action_audit rather than a new column — the same trick the idle-Unipile sweep uses for\nits warning (services/heartbeat.py). Re-running only picks up whoever was missed.\n\nDelivery is recorded per user: send_email returns False on anything but a 2xx from Resend,\nand that lands as a failed audit row. When notice carries legal weight, \"we sent it\" has to\nbe answerable per recipient, not assumed.\n\nDefaults to dry_run — the first call prints the roster so it can be eyeballed before any\nmail goes out.","operationId":"send_tos_notice_admin_tos_notice_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"List recipients without sending","default":true,"title":"Dry Run"},"description":"List recipients without sending"},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/overview":{"get":{"tags":["admin"],"summary":"Overview","description":"The single admin read: roster, today's usage so far, and the trailing window.\n\nPer-endpoint traffic is split real vs. `noise` (load-balancer scanners), the user rows\ncarry today's counters and messaging health, and `frontend` holds the same day's browser\nnumbers. The day is UTC — see the `timezone` and `date` fields.\n\nNamed /overview rather than bare /admin because the billing gate exempts the \"/admin/\"\nprefix (main.py), which the bare path would not match.","operationId":"overview_admin_overview_get","parameters":[{"name":"window","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Trailing window in days","default":7,"title":"Window"},"description":"Trailing window in days"},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Add a drill-down for one user","title":"User Id"},"description":"Add a drill-down for one user"},{"name":"posthog","in":"query","required":false,"schema":{"type":"boolean","description":"Include frontend numbers from PostHog","default":true,"title":"Posthog"},"description":"Include frontend numbers from PostHog"},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/issue-reports":{"get":{"tags":["admin"],"summary":"List Issue Reports","description":"Anonymous break reports from agents, newest first. There is no user to filter on and\nnone to follow up with — read these in aggregate (see db/models.py IssueReport).","operationId":"list_issue_reports_admin_issue_reports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to new or solved","title":"Status"},"description":"Filter to new or solved"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/issue-reports/{report_id}/solve":{"post":{"tags":["admin"],"summary":"Solve Issue Report","description":"Publish a report to the agent-facing list_solved_issues feed.\n\nThe two fields you write here are the ONLY thing that feed ever returns — the submitted\ntext stays private because nobody vetted it. Write them for an agent to read.","operationId":"solve_issue_report_admin_issue_reports__report_id__solve_post","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Report Id"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string","title":"X-Admin-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SolveIssueRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/accept-terms":{"post":{"tags":["users"],"summary":"Accept Terms","description":"Record ToS/PP assent for an authenticated user. Exempt from the assent gate so a\nnot-yet-accepted user can reach it (see _TOS_EXEMPT in main.py).","operationId":"accept_terms_accept_terms_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptTermsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/users/me":{"get":{"tags":["users"],"summary":"Get Me","operationId":"get_me_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"tags":["users"],"summary":"Delete Me","operationId":"delete_me_users_me_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/users/me/rotate-api-key":{"post":{"tags":["users"],"summary":"Rotate Api Key","operationId":"rotate_api_key_users_me_rotate_api_key_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/users/setup":{"post":{"tags":["users"],"summary":"Setup User","operationId":"setup_user_users_setup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/users/fewshots":{"patch":{"tags":["users"],"summary":"Update Fewshots","operationId":"update_fewshots_users_fewshots_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FewshotsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/users/connect-linkedin":{"post":{"tags":["users"],"summary":"Connect Linkedin","operationId":"connect_linkedin_users_connect_linkedin_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkedInRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/users/connect-linkedin-checkpoint":{"post":{"tags":["users"],"summary":"Connect Linkedin Checkpoint","operationId":"connect_linkedin_checkpoint_users_connect_linkedin_checkpoint_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckpointRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/users/connect-linkedin-url":{"get":{"tags":["users"],"summary":"Get Connect Linkedin Url","operationId":"get_connect_linkedin_url_users_connect_linkedin_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/users/disconnect-linkedin":{"post":{"tags":["users"],"summary":"Disconnect Linkedin","operationId":"disconnect_linkedin_users_disconnect_linkedin_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/upload/presign":{"post":{"tags":["users"],"summary":"Presign Video Upload","operationId":"presign_video_upload_upload_presign_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/linkedin/connections":{"get":{"tags":["users"],"summary":"Search Connections","operationId":"search_connections_linkedin_connections_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/drafts":{"get":{"tags":["drafts"],"summary":"List Drafts","operationId":"list_drafts_drafts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"pending_review","title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/drafts/{draft_id}":{"get":{"tags":["drafts"],"summary":"Get Draft","operationId":"get_draft_drafts__draft_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/drafts/{draft_id}/approve":{"post":{"tags":["drafts"],"summary":"Approve Draft","operationId":"approve_draft_drafts__draft_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/routers__api__drafts__ApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/drafts/{draft_id}/skip":{"post":{"tags":["drafts"],"summary":"Skip Draft","operationId":"skip_draft_drafts__draft_id__skip_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/drafts/{draft_id}/rewrite":{"post":{"tags":["drafts"],"summary":"Rewrite Draft","operationId":"rewrite_draft_drafts__draft_id__rewrite_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scheduled":{"get":{"tags":["scheduled"],"summary":"List Scheduled","operationId":"list_scheduled_scheduled_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["scheduled"],"summary":"Create Manual Scheduled","operationId":"create_manual_scheduled_scheduled_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleManualBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/scheduled/{send_id}":{"patch":{"tags":["scheduled"],"summary":"Reschedule Send","operationId":"reschedule_send_scheduled__send_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"send_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Send Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["scheduled"],"summary":"Cancel Scheduled","operationId":"cancel_scheduled_scheduled__send_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"send_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Send Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats":{"get":{"tags":["chats"],"summary":"List Chats","operationId":"list_chats_chats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/chats/backfill":{"post":{"tags":["chats"],"summary":"Backfill Chats","operationId":"backfill_chats_chats_backfill_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/chats/{chat_id}/messages":{"get":{"tags":["chats"],"summary":"Get Chat Messages","operationId":"get_chat_messages_chats__chat_id__messages_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/{chat_id}/thread":{"get":{"tags":["chats"],"summary":"Get Thread","description":"Local-first thread read: messages from the store, seeded once from Unipile on first\nopen, scrolled up via `before` (an ISO timestamp from the prior page's next_cursor).","operationId":"get_thread_chats__chat_id__thread_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/search":{"get":{"tags":["chats"],"summary":"Search Chats","description":"Local inbox search — message content + operator-grammar filters. No LinkedIn quota.","operationId":"search_chats_chats_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"is_unread","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Unread"}},{"name":"from_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Name"}},{"name":"company","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"}},{"name":"after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"After"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}},{"name":"newer_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Newer Days"}},{"name":"older_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Older Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/stale":{"get":{"tags":["chats"],"summary":"Find Stale Threads","description":"Threads with no activity in `days`, tagged ghosted | owed_reply.","operationId":"find_stale_threads_chats_stale_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/classify":{"get":{"tags":["chats"],"summary":"Classify Inbox","description":"Triage the inbox into buckets with confidence (cache-first, bounded per call).","operationId":"classify_inbox_chats_classify_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/{chat_id}/summary":{"get":{"tags":["chats"],"summary":"Summarize Thread","description":"Structured, cached summary of one thread.","operationId":"summarize_thread_chats__chat_id__summary_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Force"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/reply-drafts":{"get":{"tags":["chats"],"summary":"List Reply Drafts","operationId":"list_reply_drafts_chats_reply_drafts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/chats/reply-drafts/{draft_id}/approve":{"post":{"tags":["chats"],"summary":"Approve Reply Draft","operationId":"approve_reply_draft_chats_reply_drafts__draft_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveReplyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/reply-drafts/{draft_id}/rewrite":{"post":{"tags":["chats"],"summary":"Rewrite Reply Draft","operationId":"rewrite_reply_draft_chats_reply_drafts__draft_id__rewrite_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteReplyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/reply-drafts/{draft_id}/skip":{"post":{"tags":["chats"],"summary":"Skip Reply Draft","operationId":"skip_reply_draft_chats_reply_drafts__draft_id__skip_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/{chat_id}/draft-reply":{"post":{"tags":["chats"],"summary":"Draft Reply","operationId":"draft_reply_chats__chat_id__draft_reply_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftReplyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chats/{chat_id}/state":{"patch":{"tags":["chats"],"summary":"Update Chat State","operationId":"update_chat_state_chats__chat_id__state_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatStateBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/pipeline/run":{"post":{"tags":["pipelines"],"summary":"Trigger Pipeline","operationId":"trigger_pipeline_pipeline_run_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/pipeline/runs":{"get":{"tags":["pipelines"],"summary":"Get Pipeline Runs","operationId":"get_pipeline_runs_pipeline_runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments-pipeline/run":{"post":{"tags":["pipelines"],"summary":"Trigger Comment Pipeline","operationId":"trigger_comment_pipeline_comments_pipeline_run_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/comments-pipeline/run-by-company":{"post":{"tags":["pipelines"],"summary":"Trigger Company Comment Pipeline","description":"Comment drafts on recent posts by people at specific companies (max 5).","operationId":"trigger_company_comment_pipeline_comments_pipeline_run_by_company_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyCommentRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/comments-pipeline/runs":{"get":{"tags":["pipelines"],"summary":"Get Comment Pipeline Runs","operationId":"get_comment_pipeline_runs_comments_pipeline_runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-pipeline/run":{"post":{"tags":["pipelines"],"summary":"Trigger Content Pipeline","operationId":"trigger_content_pipeline_content_pipeline_run_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ContentPipelineRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/content-pipeline/runs":{"get":{"tags":["pipelines"],"summary":"List Content Pipeline Runs","operationId":"list_content_pipeline_runs_content_pipeline_runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts":{"get":{"tags":["post_drafts"],"summary":"List Post Drafts","operationId":"list_post_drafts_post_drafts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"pending_review","title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts/{draft_id}":{"get":{"tags":["post_drafts"],"summary":"Get Post Draft","operationId":"get_post_draft_post_drafts__draft_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts/{draft_id}/approve":{"post":{"tags":["post_drafts"],"summary":"Approve Post Draft","operationId":"approve_post_draft_post_drafts__draft_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveCommentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts/{draft_id}/skip":{"post":{"tags":["post_drafts"],"summary":"Skip Post Draft","operationId":"skip_post_draft_post_drafts__draft_id__skip_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts/{draft_id}/rewrite":{"post":{"tags":["post_drafts"],"summary":"Rewrite Post Draft","operationId":"rewrite_post_draft_post_drafts__draft_id__rewrite_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteCommentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/post-drafts/{draft_id}/edit":{"post":{"tags":["post_drafts"],"summary":"Edit Post Draft","operationId":"edit_post_draft_post_drafts__draft_id__edit_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditCommentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scheduled-comments":{"get":{"tags":["post_drafts"],"summary":"List Scheduled Comments","operationId":"list_scheduled_comments_scheduled_comments_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/scheduled-comments/{send_id}":{"delete":{"tags":["post_drafts"],"summary":"Cancel Scheduled Comment","operationId":"cancel_scheduled_comment_scheduled_comments__send_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"send_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Send Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-drafts":{"get":{"tags":["content_drafts"],"summary":"List Content Drafts","operationId":"list_content_drafts_content_drafts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"pending_review","title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-drafts/{draft_id}":{"get":{"tags":["content_drafts"],"summary":"Get Content Draft","operationId":"get_content_draft_content_drafts__draft_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-drafts/{draft_id}/approve":{"post":{"tags":["content_drafts"],"summary":"Approve Content Draft","operationId":"approve_content_draft_content_drafts__draft_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveContentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-drafts/{draft_id}/skip":{"post":{"tags":["content_drafts"],"summary":"Skip Content Draft","operationId":"skip_content_draft_content_drafts__draft_id__skip_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/content-drafts/{draft_id}/rewrite":{"post":{"tags":["content_drafts"],"summary":"Rewrite Content Draft","operationId":"rewrite_content_draft_content_drafts__draft_id__rewrite_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewriteContentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/replies":{"get":{"tags":["replies"],"summary":"List Replies","description":"Inbound threads needing a reply. Sourced from chats (is:unread) + any pending reply\ndraft. Shape kept compatible with the existing web Replies screen after the\nlinkedin_replies teardown — `id` is now the chat_id.","operationId":"list_replies_replies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/replies/{chat_id}/ignore":{"post":{"tags":["replies"],"summary":"Ignore Reply","description":"Archive the thread (removes it from the needs-reply view).","operationId":"ignore_reply_replies__chat_id__ignore_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/billing/create-checkout":{"post":{"tags":["billing"],"summary":"Create Checkout","operationId":"create_checkout_billing_create_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/billing/portal":{"get":{"tags":["billing"],"summary":"Billing Portal","operationId":"billing_portal_billing_portal_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/billing/status":{"get":{"tags":["billing"],"summary":"Billing Status","operationId":"billing_status_billing_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/billing/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","operationId":"stripe_webhook_billing_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/webhook/unipile-account":{"post":{"tags":["webhooks"],"summary":"Unipile Account Webhook","description":"Unipile posts here after a hosted-auth LinkedIn connection completes.","operationId":"unipile_account_webhook_webhook_unipile_account_post","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhook/linkedin-message":{"post":{"tags":["webhooks"],"summary":"Linkedin Message Webhook","operationId":"linkedin_message_webhook_webhook_linkedin_message_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/webhook/linkedin-relation":{"post":{"tags":["webhooks"],"summary":"Linkedin Relation Webhook","description":"Unipile posts here when a connection invite is accepted (new_relation, registered\nwith `source: \"users\"` — see ensure_relation_webhook, a separate hook from the message\none above since Unipile scopes webhooks by source). Not real-time: Unipile's own docs\nsay up to 8h after LinkedIn accepts, since LinkedIn has no real-time event for this.","operationId":"linkedin_relation_webhook_webhook_linkedin_relation_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/webhook/linkedin-account-status":{"post":{"tags":["webhooks"],"summary":"Linkedin Account Status Webhook","description":"Unipile posts here on account lifecycle changes (registered with\n`source: \"account_status\"` — see ensure_account_status_webhook, a third hook\nseparate from messaging/users since Unipile scopes webhooks by source).\n\nPayload shape: {\"AccountStatus\": {\"account_id\", \"account_type\", \"message\"}}\n(developer.unipile.com/docs/account-lifecycle). On a disruptive `message`\n(CREDENTIALS = invalid/missing credentials, or another restriction-type value —\nsee _DISRUPTIVE_ACCOUNT_STATUS) this writes an ActionAudit row and logs loudly so\nthe signal is captured (LIMITS.md A4 uses it to halve that day's caps), and stamps\nuser.unipile_status so services/linkedin_gate can turn the next LinkedIn call into a\n\"reconnect\" error instead of a silent failure against a dead account. A healthy message\nclears it back to None — this is what makes it self-heal once Unipile reports the account\nworking again (e.g. RECONNECTED after the user reconnects).\n\nA `_READY_ACCOUNT_STATUS` message (OK/SYNC_SUCCESS) additionally clears\nuser.unipile_syncing_since — the post-connect sync gate from ACCT_INITIALIZATION.md — and,\nonly on that syncing→ready transition (never on a later steady-state `ok` for an account\nthat was already ready), fires the two on-connect kickoffs\n(_kickoff_self_profile/_kickoff_chat_backfill) that used to run at connect time itself,\nbefore Unipile had necessarily finished setting the account up.","operationId":"linkedin_account_status_webhook_webhook_linkedin_account_status_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/discovery/search":{"post":{"tags":["discovery"],"summary":"Discover Prospects","operationId":"discover_prospects_discovery_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/activity":{"get":{"tags":["activity"],"summary":"List Activity","description":"One page of this user's activity, newest first. Paginate with the returned next_cursor.","operationId":"list_activity_activity_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"surface","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"linkedin | iridium","title":"Surface"},"description":"linkedin | iridium"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"success | failed | skipped","title":"Status"},"description":"success | failed | skipped"},{"name":"action_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity/latest":{"get":{"tags":["activity"],"summary":"Latest Activity","description":"The most recent action, with full detail. Returns {\"item\": null} on a new account.","operationId":"latest_activity_activity_latest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/chat/history":{"get":{"tags":["chat"],"summary":"Chat History","operationId":"chat_history_chat_history_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"tags":["chat"],"summary":"Clear Chat History","operationId":"clear_chat_history_chat_history_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/chat":{"post":{"tags":["chat"],"summary":"Chat","operationId":"chat_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/email/unsubscribe":{"get":{"tags":["email"],"summary":"Unsubscribe","operationId":"unsubscribe_email_unsubscribe_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/version":{"get":{"summary":"Version","operationId":"version_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/glama.json":{"get":{"summary":"Glama","description":"Proves listing ownership to glama.ai. Email must match a Glama account.","operationId":"glama__well_known_glama_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/mcp/server-card.json":{"get":{"summary":"Server Card","description":"Static capability metadata for directory crawlers (Smithery et al).\n\nTheir scanners call tools/list over the transport, which is auth-gated, so they get a 401\nand index nothing — the failure recorded in glama.md. Serving this card is the documented\nway past that WITHOUT opening /mcp to unauthenticated introspection: the 401 there is what\nstarts OAuth discovery for real clients and must stay.\n\n`_mcp_http.tools` is the same list the transport's own list_tools handler returns, so the\ncard cannot drift from what an authenticated client sees. Tool names and schemas are public\nsurface by intent; tools/call remains gated.\n\n`description` and `prompts` come from the same two sources the transport uses, for the same\nreason. A crawler reading only this card is the first impression for most directories, and 53\ntool names with no framing is not one — the prompts are what say who Iridium is for.","operationId":"server_card__well_known_mcp_server_card_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Root","description":"Entry point for an agent that has the hostname and nothing else.\n\nGET / was a 404, which is a dead end: the discovery documents all exist but you had to\nalready know their paths to find them. Every URL below is a document that was already\nbeing served, so this adds a way in and no new surface. Unauthenticated by design, and\nit reveals nothing that /.well-known does not.\n\nThe API catalogue and auth.md live on the public site, not here, because that is the\norigin that publishes the agent-facing set. One copy, linked from both.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AcceptTermsRequest":{"properties":{"agree":{"type":"boolean","title":"Agree"},"version":{"type":"string","title":"Version"}},"type":"object","required":["agree","version"],"title":"AcceptTermsRequest"},"ApproveCommentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"comment_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment Text"},"comment_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment At"}},"type":"object","required":["handle"],"title":"ApproveCommentDraftRequest"},"ApproveCommentRequest":{"properties":{"comment_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment At"},"comment_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment Text"}},"type":"object","title":"ApproveCommentRequest"},"ApproveContentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"post_text":{"type":"string","title":"Post Text"},"scheduled_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled At"}},"type":"object","required":["handle","post_text"],"title":"ApproveContentDraftRequest"},"ApproveContentRequest":{"properties":{"post_text":{"type":"string","title":"Post Text"},"scheduled_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled At"}},"type":"object","required":["post_text"],"title":"ApproveContentRequest"},"ApproveDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"override_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Text"},"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"}},"type":"object","required":["handle"],"title":"ApproveDraftRequest"},"ApproveReplyBody":{"properties":{"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"},"override_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Text"}},"type":"object","title":"ApproveReplyBody"},"ApproveReplyDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"},"override_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Text"}},"type":"object","required":["handle"],"title":"ApproveReplyDraftRequest"},"Body_authorize_submit_oauth_authorize_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"api_key":{"type":"string","title":"Api Key"}},"type":"object","required":["session_id","api_key"],"title":"Body_authorize_submit_oauth_authorize_post"},"Body_token_oauth_token_post":{"properties":{"grant_type":{"type":"string","title":"Grant Type"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"},"redirect_uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Uri"},"code_verifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Verifier"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret"},"refresh_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"},"resource":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource"}},"type":"object","required":["grant_type"],"title":"Body_token_oauth_token_post"},"CancelScheduledRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"CancelScheduledRequest"},"ChatRequest":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"ChatRequest"},"ChatStateBody":{"properties":{"state":{"type":"string","title":"State"}},"type":"object","required":["state"],"title":"ChatStateBody"},"CheckoutRequest":{"properties":{"plan":{"type":"string","title":"Plan"},"seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats"},"arl_consent":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Arl Consent"}},"type":"object","required":["plan"],"title":"CheckoutRequest"},"CheckpointRequest":{"properties":{"account_id":{"type":"string","title":"Account Id"},"code":{"type":"string","title":"Code"}},"type":"object","required":["account_id","code"],"title":"CheckpointRequest"},"ClassifyInboxRequest":{"properties":{"taxonomy":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Taxonomy"},"limit":{"type":"integer","title":"Limit","default":25}},"type":"object","title":"ClassifyInboxRequest"},"CompanyCommentRequest":{"properties":{"companies":{"items":{"type":"string"},"type":"array","title":"Companies"},"n":{"type":"integer","title":"N","default":3},"date_posted":{"type":"string","title":"Date Posted","default":"past_week"}},"type":"object","required":["companies"],"title":"CompanyCommentRequest"},"ConnectLinkedInCheckpointRequest":{"properties":{"account_id":{"type":"string","title":"Account Id"},"code":{"type":"string","title":"Code"}},"type":"object","required":["account_id","code"],"title":"ConnectLinkedInCheckpointRequest"},"ConnectLinkedInCredentialsRequest":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","required":["username","password"],"title":"ConnectLinkedInCredentialsRequest"},"ConnectLinkedInLiAtRequest":{"properties":{"li_at":{"type":"string","title":"Li At"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","required":["li_at"],"title":"ConnectLinkedInLiAtRequest"},"ConnectLinkedInRequest":{"properties":{"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"li_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Li At"},"unipile_account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unipile Account Id"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","title":"ConnectLinkedInRequest"},"ContentPipelineRequest":{"properties":{"paragraph_count":{"type":"integer","title":"Paragraph Count","default":3},"tone":{"type":"string","title":"Tone","default":"Direct"},"mood":{"type":"string","title":"Mood","default":"Informational"}},"type":"object","title":"ContentPipelineRequest"},"CreateSequenceRequest":{"properties":{"name":{"type":"string","title":"Name"},"steps":{"items":{"$ref":"#/components/schemas/SequenceStepIn"},"type":"array","title":"Steps"}},"type":"object","required":["name","steps"],"title":"CreateSequenceRequest"},"CreateUserRequest":{"properties":{"email":{"type":"string","title":"Email"},"display_name":{"type":"string","title":"Display Name"},"service_client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Client Id"},"allow_undeliverable":{"type":"boolean","title":"Allow Undeliverable","default":false}},"type":"object","required":["email","display_name"],"title":"CreateUserRequest"},"DiscoverProspectsRequest":{"properties":{"query":{"type":"string","title":"Query"},"location":{"type":"string","title":"Location"},"limit":{"type":"integer","title":"Limit","default":5},"hiring_only":{"type":"boolean","title":"Hiring Only","default":false},"open_to_work_only":{"type":"boolean","title":"Open To Work Only","default":false}},"type":"object","required":["query","location"],"title":"DiscoverProspectsRequest"},"DiscoverRequest":{"properties":{"query":{"type":"string","title":"Query"},"location":{"type":"string","title":"Location"},"limit":{"type":"integer","title":"Limit","default":5}},"type":"object","required":["query","location"],"title":"DiscoverRequest"},"DraftReplyBody":{"properties":{"intent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Intent"}},"type":"object","title":"DraftReplyBody"},"DraftReplyRequest":{"properties":{"chat_handle":{"type":"string","title":"Chat Handle"},"intent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Intent"}},"type":"object","required":["chat_handle"],"title":"DraftReplyRequest"},"EditCommentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"updated_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated Content"}},"type":"object","required":["handle"],"title":"EditCommentDraftRequest"},"EditCommentRequest":{"properties":{"updated_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated Content"}},"type":"object","title":"EditCommentRequest"},"EnrollProspectRequest":{"properties":{"prospect_handle":{"type":"string","title":"Prospect Handle"},"sequence_handle":{"type":"string","title":"Sequence Handle"}},"type":"object","required":["prospect_handle","sequence_handle"],"title":"EnrollProspectRequest"},"FewshotsRequest":{"properties":{"stage4_examples":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Stage4 Examples"}},"type":"object","required":["stage4_examples"],"title":"FewshotsRequest"},"FindProspectsRequest":{"properties":{"query":{"type":"string","title":"Query"},"location":{"type":"string","title":"Location"},"limit":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","default":25},"company":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Company"},"headline_keywords":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Headline Keywords"},"exclude_headline_keywords":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Exclude Headline Keywords"},"network_distance":{"anyOf":[{"items":{"type":"string","enum":["DISTANCE_1","DISTANCE_2","DISTANCE_3"]},"type":"array"},{"type":"null"}],"title":"Network Distance"},"hiring":{"type":"boolean","title":"Hiring","default":false},"open_to_work":{"type":"boolean","title":"Open To Work","default":false},"posted_recently":{"type":"boolean","title":"Posted Recently","default":false},"max_touches":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Max Touches","default":0},"background_touches":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Background Touches","default":0},"want_notes":{"type":"boolean","title":"Want Notes","default":false}},"type":"object","required":["query","location"],"title":"FindProspectsRequest"},"FindStaleThreadsRequest":{"properties":{"days":{"type":"integer","title":"Days","default":7},"limit":{"type":"integer","title":"Limit","default":50}},"type":"object","title":"FindStaleThreadsRequest"},"GetDraftDetailRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"GetDraftDetailRequest"},"GetLinkedInProfileRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"linkedin_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Id"},"iridium_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iridium Handle"},"linkedin_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Linkedin Ids"},"iridium_handles":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Iridium Handles"},"company":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Company"},"education":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Education"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"headline_keywords":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Headline Keywords"}},"type":"object","title":"GetLinkedInProfileRequest"},"GetThreadRequest":{"properties":{"chat_handle":{"type":"string","title":"Chat Handle"},"before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"},"limit":{"type":"integer","title":"Limit","default":10}},"type":"object","required":["chat_handle"],"title":"GetThreadRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LoginRequest":{"properties":{"api_key":{"type":"string","title":"Api Key"}},"type":"object","required":["api_key"],"title":"LoginRequest"},"RefreshRequest":{"properties":{"refresh_token":{"type":"string","title":"Refresh Token"}},"type":"object","required":["refresh_token"],"title":"RefreshRequest"},"ReportIssueRequest":{"properties":{"what_broke":{"type":"string","maxLength":200,"title":"What Broke","description":"What failed, in one line. No names, companies, message text, emails, URLs, or Iridium handles — the field is rejected if it contains any."},"how_it_happened":{"type":"string","maxLength":200,"title":"How It Happened","description":"The steps that led to it, in one line. Same rule: describe the shape of the failure, never the data it happened to."},"frequency":{"type":"string","enum":["first_time","sometimes","every_time"],"title":"Frequency","description":"How often you have seen this failure."},"importance":{"type":"string","enum":["nice_to_have","important","blocking"],"title":"Importance","description":"How much the broken workflow matters to the user you are serving."}},"type":"object","required":["what_broke","how_it_happened","frequency","importance"],"title":"ReportIssueRequest"},"RescheduleBody":{"properties":{"send_at":{"type":"string","title":"Send At"}},"type":"object","required":["send_at"],"title":"RescheduleBody"},"RescheduleRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"send_at":{"type":"string","title":"Send At"}},"type":"object","required":["handle","send_at"],"title":"RescheduleRequest"},"RewriteCommentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"instruction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instruction"}},"type":"object","required":["handle"],"title":"RewriteCommentDraftRequest"},"RewriteCommentRequest":{"properties":{"instruction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instruction"}},"type":"object","title":"RewriteCommentRequest"},"RewriteContentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"instruction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instruction"}},"type":"object","required":["handle"],"title":"RewriteContentDraftRequest"},"RewriteContentRequest":{"properties":{"instruction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instruction"}},"type":"object","title":"RewriteContentRequest"},"RewriteDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"instruction":{"type":"string","title":"Instruction"}},"type":"object","required":["handle","instruction"],"title":"RewriteDraftRequest"},"RewriteReplyBody":{"properties":{"instruction":{"type":"string","title":"Instruction"}},"type":"object","required":["instruction"],"title":"RewriteReplyBody"},"RewriteReplyDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"},"instruction":{"type":"string","title":"Instruction"}},"type":"object","required":["handle","instruction"],"title":"RewriteReplyDraftRequest"},"RewriteRequest":{"properties":{"instruction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instruction"}},"type":"object","title":"RewriteRequest"},"ScheduleChatReplyRequest":{"properties":{"chat_handle":{"type":"string","title":"Chat Handle"},"text":{"type":"string","title":"Text"},"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"}},"type":"object","required":["chat_handle","text"],"title":"ScheduleChatReplyRequest"},"ScheduleDmToPersonRequest":{"properties":{"name":{"type":"string","title":"Name"},"text":{"type":"string","title":"Text"},"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"},"profile_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Handle"},"message_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message Type"}},"type":"object","required":["name","text"],"title":"ScheduleDmToPersonRequest"},"ScheduleManualBody":{"properties":{"linkedin_id":{"type":"string","title":"Linkedin Id"},"display_name":{"type":"string","title":"Display Name"},"note_text":{"type":"string","title":"Note Text"},"send_at":{"type":"string","title":"Send At"},"message_type":{"type":"string","title":"Message Type","default":"direct_message"},"chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Chat Id"},"video_r2_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video R2 Key"}},"type":"object","required":["linkedin_id","display_name","note_text","send_at"],"title":"ScheduleManualBody"},"ScheduleManualDMRequest":{"properties":{"chat_handle":{"type":"string","title":"Chat Handle"},"text":{"type":"string","title":"Text"},"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"},"message_type":{"type":"string","title":"Message Type","default":"direct_message"}},"type":"object","required":["chat_handle","text"],"title":"ScheduleManualDMRequest"},"SearchChatsRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"q":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"},"is_unread":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Unread"},"from_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Name"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"After"},"before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"},"newer_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Newer Days"},"older_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Older Days"},"limit":{"type":"integer","title":"Limit","default":50}},"type":"object","title":"SearchChatsRequest"},"SearchPostsByTopicRequest":{"properties":{"keywords":{"type":"string","title":"Keywords"},"author_company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Company"},"author_keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Keywords"},"date_posted":{"type":"string","enum":["past_day","past_week","past_month"],"title":"Date Posted","default":"past_month"},"posted_by":{"anyOf":[{"type":"string","enum":["first_connections","people_you_follow"]},{"type":"null"}],"title":"Posted By"},"limit":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Limit","default":10}},"type":"object","required":["keywords"],"title":"SearchPostsByTopicRequest"},"SearchProspectsRequest":{"properties":{"query":{"type":"string","title":"Query"},"location":{"type":"string","title":"Location"},"limit":{"type":"integer","title":"Limit","default":20},"hiring_only":{"type":"boolean","title":"Hiring Only","default":false},"open_to_work_only":{"type":"boolean","title":"Open To Work Only","default":false},"posted_recently":{"type":"boolean","title":"Posted Recently","default":false},"open_to_work":{"type":"boolean","title":"Open To Work","default":false}},"type":"object","required":["query","location"],"title":"SearchProspectsRequest"},"SequenceStepIn":{"properties":{"delay_days":{"type":"integer","title":"Delay Days"},"kind":{"type":"string","title":"Kind"},"template":{"type":"string","title":"Template"}},"type":"object","required":["delay_days","kind","template"],"title":"SequenceStepIn"},"SettingsUpdate":{"properties":{"current_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Role","description":"The user's role and company, e.g. 'Founder at Acme'."},"credential_lead":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credential Lead","description":"One or two sentences on why the user is credible. Opens outreach messages."},"networking_goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Networking Goal","description":"What the user wants from the people they reach, e.g. 'find design partners'."},"outreach_intent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Outreach Intent","description":"Message strategy: 'sell', 'connection' or 'referral'."},"style_examples":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Style Examples","description":"Up to 3 approved note texts to imitate."},"target_personas":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Target Personas","description":"Job titles to search for, e.g. ['VP of Engineering', 'Founder']."},"search_keywords":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Search Keywords","description":"Keywords searched alongside the personas, e.g. ['AI platform']."},"target_cities":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Target Cities","description":"Cities to search in, e.g. ['San Francisco', 'New York']."},"target_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Target Countries","description":"Countries to search in, e.g. ['United States']."},"daily_outreach_cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Outreach Cap","description":"Connection requests per day, clamped to 25."},"comment_topics":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Comment Topics","description":"Subjects searched when finding posts to comment on."},"daily_comment_cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Comment Cap","description":"Comments per day, clamped to 25."},"network_post_pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Network Post Pct","description":"Percent of comments sourced from in-network posts; the rest come from keyword search."},"new_position_pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"New Position Pct","description":"Percent of the daily cap allowed for new-role announcement posts."},"comment_author_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Comment Author Countries","description":"Only comment on posts by authors in these countries. Empty means no filter."},"filter_cert_posts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Filter Cert Posts","description":"Skip 'I just earned/completed/certified' posts."},"filter_hiring_posts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Filter Hiring Posts","description":"Skip 'we're hiring / open role' posts."},"filter_job_seeking_posts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Filter Job Seeking Posts","description":"Skip 'open to work / looking for my next role' posts."},"comment_hiring_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Comment Hiring Only","description":"Only comment on posts tagged #hiring — opt-in, the inverse of filter_hiring_posts."},"content_topics":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Content Topics","description":"Subjects the user's own posts are drafted about."},"voice_dna":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice Dna","description":"Writing rules applied to every draft, e.g. 'no buzzwords, direct, max 2 sentences'."},"onboarding_goals":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Onboarding Goals","description":"Which pipelines are active. Any of ['commenting', 'content', 'outreach']."},"data_retention_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Data Retention Days","description":"Days before scraped profiles and drafts are purged."}},"type":"object","title":"SettingsUpdate","description":"Every user-writable setting, in one model — the single description of these columns.\n\nBoth transports bind to this: POST /users/setup (web) and POST /mcp/tools/update_settings\n(MCP + the in-app agent). Field names are the column names on purpose, so the field an\nerror names — outreach_blocker in routers/shared/pipelines.py prints raw column names —\nis the argument an agent sends back to fix it, with nothing to translate in between.\n\nThe descriptions are not decoration: fastapi-mcp builds the remote MCP input schema from\nthis model (main.py FastApiMCP), and services/agent_tools reads model_json_schema() for\nthe in-app agent, so this text is what both agents see. Plain `#` comments are invisible\nto both.\n\nEvery field is optional; only the ones passed are written (model_dump(exclude_none=True))."},"SkipCommentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"SkipCommentDraftRequest"},"SkipContentDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"SkipContentDraftRequest"},"SkipDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"SkipDraftRequest"},"SkipReplyDraftRequest":{"properties":{"handle":{"type":"string","title":"Handle"}},"type":"object","required":["handle"],"title":"SkipReplyDraftRequest"},"SocialLoginRequest":{"properties":{"supabase_token":{"type":"string","title":"Supabase Token"}},"type":"object","required":["supabase_token"],"title":"SocialLoginRequest"},"SolveIssueRequest":{"properties":{"public_title":{"type":"string","title":"Public Title"},"resolution":{"type":"string","title":"Resolution"}},"type":"object","required":["public_title","resolution"],"title":"SolveIssueRequest"},"SummarizeThreadRequest":{"properties":{"chat_handle":{"type":"string","title":"Chat Handle"},"force":{"type":"boolean","title":"Force","default":false}},"type":"object","required":["chat_handle"],"title":"SummarizeThreadRequest"},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"refresh_token":{"type":"string","title":"Refresh Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"}},"type":"object","required":["access_token","refresh_token"],"title":"TokenResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"routers__api__drafts__ApproveRequest":{"properties":{"send_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send At"},"note_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note Text"}},"type":"object","title":"ApproveRequest"},"routers__api__oauth__ApproveRequest":{"properties":{"redirect_uri":{"type":"string","title":"Redirect Uri"},"code_challenge":{"type":"string","title":"Code Challenge"},"client_id":{"type":"string","title":"Client Id","default":""},"state":{"type":"string","title":"State","default":""},"code_challenge_method":{"type":"string","title":"Code Challenge Method","default":"S256"},"resource":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource"}},"type":"object","required":["redirect_uri","code_challenge"],"title":"ApproveRequest"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}