{"openapi":"3.1.0","info":{"title":"Hundrads","description":"The safe middle layer between your AI agent and your marketing accounts.\n\n**The contract in one line:** agents create drafts and read data; humans\napprove in the dashboard; only approval touches a platform.\n\nThree families, one human gate each:\n\n| Gate | Ads | Content | Proposals |\n|---|---|---|---|\n| Draft kinds | `meta_ad` | `threads_post` · `facebook_post` · `instagram_post` · `linkedin_post` · `telegram_post` · `newsletter` | `budget_change` · `status_change` · `ad_edit` · `comment_reply` · `comment_hide` |\n| Lands in | Review queue | Schedule tab (at its proposed `scheduled_at`) | Review queue |\n| On approve | Pushed to Meta **PAUSED** — RM0 until the user acts in Ads Manager | Auto-posted **at the slot** by the scheduler — never before, never without approve | The change is **executed** on the live object — carries the user's explicit (or standing-policy) consent |\n\nReads are ungated and rich: live ads, insights with breakdowns, comments,\naudiences, leads, budget pacing — plus the intel layer no raw platform API\nhas (benchmarks, decisions, outcomes, alerts, unified performance, policies).\nStudy first, then propose with the numbers in `reason`.\n\n**Auth:** every `/v1/*` request needs `Authorization: Bearer $HUNDRADS_API_KEY`\n(or `X-API-Key: ...`).\n\n**For AI agents:** fetch [`/llms.txt`](/llms.txt) — the full reference as\nplain markdown (endpoints, payload schemas, etiquette) — or\n[`/openapi.json`](/openapi.json) for the machine-readable schema.\nAgents may revise or withdraw their own pending drafts. There is no\nagent-facing approve or send endpoint; those human gates remain in the\ndashboard.\n\n**Errors:** `401` bad key · `422` validation (the message says exactly what\nto fix — one corrected retry is fine) · `502` upstream platform error\n(relay it; don't retry blindly).\n","version":"0.1.0"},"paths":{"/v1/agent/notes":{"post":{"tags":["Agent"],"summary":"Add Note","description":"Save a durable OPERATIONAL note — judgment or intent you want your future\nself to remember: \"scaled too fast on 6/10, CTR decayed — ease the steps\",\n\"Bukan-Tech angle keeps getting disapproved on akala, stop retrying\". It\nshows up in the dashboard for the human to read and prune.\n\nNEVER store a fact a query can answer — ad status, spend, ROAS, what's\npending. Those are read live from /agent/ops or the /v1/ads /v1/drafts\nendpoints; a cached fact here just goes stale (this is exactly why the old\nlocal state file failed).","operationId":"add_note_v1_agent_notes_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentNoteIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Add Note V1 Agent Notes Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Agent"],"summary":"List Notes","description":"Your operational notes for this workspace, newest first. Read these at\nthe start of a pass — they're what you learned last time.","operationId":"list_notes_v1_agent_notes_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Notes V1 Agent Notes Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agent/notes/{note_id}":{"delete":{"tags":["Agent"],"summary":"Delete Note","description":"Delete a note once it's no longer true (the learning was wrong, or the\nsituation it described is resolved).","operationId":"delete_note_v1_agent_notes__note_id__delete","parameters":[{"name":"note_id","in":"path","required":true,"schema":{"type":"string","title":"Note Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Note V1 Agent Notes  Note Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agent/ops":{"get":{"tags":["Agent"],"summary":"Ops","description":"Your working state, DERIVED LIVE (nothing stored): the backlog and\ncounters the no-pile-up and cost guards need. Re-fetch every pass — it\nreflects reality the instant the human acts in the dashboard.\n\n- pending_activation: ads you staged that are still PAUSED (approved but not\n  turned on). NON-EMPTY = HOLD, don't stage more; it clears itself when the\n  human activates or deletes them.\n- pending_drafts: drafts still awaiting the human in the review queue.\n- meta_ad_drafts_today: new test ads staged today (anti-churn cap).\n- poster_spend_today_usd: today's AI poster cost (daily cap guard).\n- last_pass: when you last hit the API.","operationId":"ops_v1_agent_ops_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Ops V1 Agent Ops Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agent/notify-check":{"post":{"tags":["Agent"],"summary":"Notify Check","description":"Ask the server whether to send a notification — and record the send in\none call. Pass a fingerprint built from CURRENT data; the server remembers\nwhat it last delivered per (scope, key) and returns {send, reason}. Send\nonly when send=true. This replaces tracking alerts_sent / last_digest_hash\nyourself — the server owns \"already said this / nothing changed\".","operationId":"notify_check_v1_agent_notify_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifyCheckIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Notify Check V1 Agent Notify Check Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/drafts":{"post":{"tags":["Drafts"],"summary":"Create Draft","description":"Submit a draft or proposal for human review. Returns it + advice.\n\nThree families, one human gate each:\n- ads (meta_ad / google_search_ad): lands in the review queue; approve\n  pushes to the platform PAUSED.\n- content (threads_post / facebook_post / instagram_post / linkedin_post /\n  telegram_post / newsletter): lands on the Schedule tab at its proposed scheduled_at;\n  approve arms the auto-post.\n- proposals (budget_change / status_change / ad_edit / comment_reply /\n  comment_hide on Meta; google_budget_change / google_status_change /\n  google_negative_keyword on Google): a CHANGE to a live object; approve\n  EXECUTES it. Always fill `reason` with the numbers that justify it —\n  the user decides on that. Rules in GET /v1/policies may auto-approve\n  low-risk kinds.\n\nCheck back with GET /v1/drafts/{id} — on `rejected`, read `review_note`\n(the user's reason) before drafting a replacement.","operationId":"create_draft_v1_drafts_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Draft V1 Drafts Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Drafts"],"summary":"List Drafts","description":"List drafts newest first. Filter by status to read review feedback:\nstatus=rejected gives the user's review_note per draft.","operationId":"list_drafts_v1_drafts_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"","title":"Status"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kind"}},{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Skip"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Drafts V1 Drafts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/schedule":{"get":{"tags":["Content"],"summary":"Schedule","description":"The content schedule — every post still in flight (pending review,\napproved-and-waiting, or mid-send), oldest slot first.\n\nCall this BEFORE proposing a scheduled_at on a new content draft: pick a\nslot that doesn't collide with what's already booked (leave hours between\nposts on the same channel).","operationId":"schedule_v1_schedule_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Schedule V1 Schedule Get"}}}}}}},"/v1/drafts/{draft_id}":{"get":{"tags":["Drafts"],"summary":"Get Draft","operationId":"get_draft_v1_drafts__draft_id__get","parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Draft V1 Drafts  Draft Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Drafts"],"summary":"Update Draft","description":"Revise a pending draft without creating a duplicate review item.\n\n`payload` is a partial object: supplied fields replace their stored values,\nomitted fields stay unchanged. The merged, complete payload is validated\nthrough the same kind-specific schema as POST /v1/drafts. A draft's kind is\nimmutable, and editing stops as soon as the reviewer has acted.","operationId":"update_draft_v1_drafts__draft_id__patch","parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","title":"Draft Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Draft V1 Drafts  Draft Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Drafts"],"summary":"Withdraw Draft","description":"Withdraw a draft you no longer stand behind (stale numbers, better\nidea). Only `pending` drafts can be withdrawn — once the user acted on\nit, the decision is theirs.","operationId":"withdraw_draft_v1_drafts__draft_id__delete","parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"type":"string","title":"Draft Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Withdraw Draft V1 Drafts  Draft Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ads":{"get":{"tags":["Ads"],"summary":"Live Ads","description":"Every ad in the brand's account with live status + numbers.\n\nplatform selects the ad platform — meta (default) or google. status\nfilters to running | paused | other. q matches name/campaign/copy.\ndate_preset sets the metrics window (spend/purchases/roas/impressions/...):\nmaximum (lifetime, default) | today | yesterday | last_7d | last_14d |\nlast_30d | this_month | last_month — every ad still lists; ones with no\ndelivery in the window just read zero. refresh=true forces a fresh Meta\nsweep (otherwise a <15-min-old snapshot is served). Google rows come live\nfrom the API every call (no snapshot; refresh is a no-op), carry lifetime\nmetrics only (keep date_preset at maximum), and list RSA\nheadlines/descriptions. Use the returned ids to propose changes via\nPOST /v1/drafts (kinds budget_change/status_change/ad_edit — or the\ngoogle_* proposal kinds for Google rows).","operationId":"live_ads_v1_ads_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"","title":"Status"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"date_preset","in":"query","required":false,"schema":{"type":"string","default":"maximum","title":"Date Preset"}},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Live Ads V1 Ads Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ads/pending-activation":{"get":{"tags":["Ads"],"summary":"Pending Activation","description":"Ads you staged (published meta_ad drafts) whose live ad is still PAUSED —\nthe real approved-but-unactivated backlog, DERIVED fresh from drafts ⋈ the\nlive snapshot. A non-empty list means the human hasn't turned them on yet;\nit empties itself the moment they activate or delete them (no stale state).\nCheck this in the no-pile-up guard instead of remembering a 'staged' list.\nMeta-only — Google has no snapshot to join against in v1.","operationId":"pending_activation_v1_ads_pending_activation_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Pending Activation V1 Ads Pending Activation Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ads/{ad_id}":{"get":{"tags":["Ads"],"summary":"Live Ad","description":"One live ad, full detail, from the snapshot. Meta-only — Google rows\nalready come back full-detail from GET /v1/ads?platform=google.","operationId":"live_ad_v1_ads__ad_id__get","parameters":[{"name":"ad_id","in":"path","required":true,"schema":{"type":"string","title":"Ad Id"}},{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Live Ad V1 Ads  Ad Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/budget/pacing":{"get":{"tags":["Ads"],"summary":"Budget Pacing","description":"Today's spend vs daily budget per active ad set, with a projection.\n\npace > 1.0 = on track to exceed the daily budget. Check this before\nproposing budget increases, and to catch runaway spend mid-day.","operationId":"budget_pacing_v1_budget_pacing_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Budget Pacing V1 Budget Pacing Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/library/ads":{"get":{"tags":["Ads"],"summary":"Library Ads","description":"Search past ads. Ranks by proven signal (default: most spend).\nplatform selects the archive — meta (default) or google (the synced\nGoogle Ads RSA rows, same filters/sort).","operationId":"library_ads_v1_library_ads_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"spend","title":"Sort"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}},{"name":"min_spend","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Spend"}},{"name":"min_ctr","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Ctr"}},{"name":"min_roas","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Roas"}},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Active Only"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Library Ads V1 Library Ads Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/library/posts":{"get":{"tags":["Ads"],"summary":"Library Posts","description":"Search the Telegram channel post archive (newest first). This is the\nTelegram voice library — match real tone + study what got reactions\nbefore drafting. `exclude` is a regex; matching posts are dropped.","operationId":"library_posts_v1_library_posts_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"min_reactions","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Reactions"}},{"name":"min_views","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Views"}},{"name":"min_replies","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Replies"}},{"name":"min_engagement","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Engagement"}},{"name":"exclude","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exclude"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Library Posts V1 Library Posts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/library/threads":{"get":{"tags":["Ads"],"summary":"Library Threads","description":"Search our own Threads posts (newest first). roots_only=true returns\none row per logical post with its self-reply chain attached under\n`chain`; false browses every individual post flat.","operationId":"library_threads_v1_library_threads_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"roots_only","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Roots Only"}},{"name":"min_views","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Views"}},{"name":"min_likes","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Likes"}},{"name":"min_engagement","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Engagement"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Library Threads V1 Library Threads Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/library/broadcasts":{"get":{"tags":["Ads"],"summary":"Library Broadcasts","description":"Search our sent email newsletters (Kit broadcasts), newest first.\nThe newsletter voice library — open/click rates are the performance\nsignal. with_content=true includes the raw HTML (the stripped `text`\nis usually what you want).","operationId":"library_broadcasts_v1_library_broadcasts_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}},{"name":"min_open_rate","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Open Rate"}},{"name":"min_click_rate","in":"query","required":false,"schema":{"type":"number","default":0,"title":"Min Click Rate"}},{"name":"with_content","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"With Content"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Library Broadcasts V1 Library Broadcasts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/library/refresh":{"post":{"tags":["Ads"],"summary":"Library Refresh","description":"Pull fresh data from the platform into the library (incremental by\ndefault). One endpoint, four targets: ads (Meta, or Google Ads with\nplatform=google), posts (Telegram), threads (Threads Graph API),\nbroadcasts (Kit). Slow — platform round-trips — so call it once at the\nstart of a session, not per query.","operationId":"library_refresh_v1_library_refresh_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Library Refresh V1 Library Refresh Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/insights":{"get":{"tags":["Ads"],"summary":"Insights","description":"Live spend/clicks/ROAS for a brand, straight from the platform (not cached).\n\nplatform selects the ad platform — meta (default) or google. breakdown\nsplits rows by a dimension — age, gender, country, region,\npublisher_platform, platform_position, device_platform, or\nhourly_stats_aggregated_by_advertiser_time_zone (Meta only for now).\ntime_increment turns totals into a time series (1 = daily, 7, 28, monthly;\nGoogle supports 1 only). Combine them to answer \"which placement is dying\nthis week\" in one call. Google levels: account | campaign | ad_group | ad.","operationId":"insights_v1_insights_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"date_preset","in":"query","required":false,"schema":{"type":"string","default":"last_30d","title":"Date Preset"}},{"name":"level","in":"query","required":false,"schema":{"type":"string","default":"campaign","title":"Level"}},{"name":"breakdown","in":"query","required":false,"schema":{"type":"string","default":"","title":"Breakdown"}},{"name":"time_increment","in":"query","required":false,"schema":{"type":"string","default":"","title":"Time Increment"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Insights V1 Insights Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/insights/compare":{"get":{"tags":["Ads"],"summary":"Compare","description":"Side-by-side insights for 2-5 ads/adsets/campaigns — the A/B verdict\nin one call. object_ids is comma-separated Meta ids (from GET /v1/ads).","operationId":"compare_v1_insights_compare_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"object_ids","in":"query","required":true,"schema":{"type":"string","title":"Object Ids"}},{"name":"date_preset","in":"query","required":false,"schema":{"type":"string","default":"last_7d","title":"Date Preset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Compare V1 Insights Compare Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/search-terms":{"get":{"tags":["Ads"],"summary":"Search Terms","description":"What people actually typed before clicking a Google Search ad\n(Google-only — Meta has no equivalent report). This is the feedstock for\ngoogle_negative_keyword proposals: scan for irrelevant queries eating\nspend and propose them as negatives via POST /v1/drafts.","operationId":"search_terms_v1_search_terms_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"date_preset","in":"query","required":false,"schema":{"type":"string","default":"last_30d","title":"Date Preset"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Search Terms V1 Search Terms Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/comments":{"get":{"tags":["Engagement"],"summary":"Comments","description":"Comments on the brand page's recent posts (incl. promoted posts).\n\nanswered=false rows are the inbox: nobody from the page replied yet.\nTo act, file a comment_reply or comment_hide draft with the comment_id —\nthe user approves it (or a policy rule auto-approves) before anything\nis published.","operationId":"comments_v1_comments_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"unanswered","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unanswered"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Comments V1 Comments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audiences":{"get":{"tags":["Engagement"],"summary":"Audiences","description":"Custom/lookalike audiences in the brand's ad account, with sizes.","operationId":"audiences_v1_audiences_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Audiences V1 Audiences Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/leads":{"get":{"tags":["Engagement"],"summary":"Leads","description":"Lead-gen forms (default) or one form's recent submissions (form_id=).","operationId":"leads_v1_leads_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"form_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Form Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Leads V1 Leads Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/benchmarks":{"get":{"tags":["Intel"],"summary":"Benchmarks","description":"The account's OWN historical norms (spend-weighted CTR/CPC/CPM/ROAS +\ntop-quartile thresholds). Compare a live ad against these, not against\ngeneric industry numbers — this is what 'good' means HERE. platform\nselects the archive — meta (default) or google (synced google_ads rows,\nsame math).","operationId":"benchmarks_v1_benchmarks_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"meta","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Benchmarks V1 Benchmarks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/decisions":{"get":{"tags":["Intel"],"summary":"Decisions","description":"The user's approve/reject history with reasons — the fastest way to\nlearn their taste. review_note on rejections says exactly what to change;\napproved_by shows which proposals a policy rule auto-cleared.","operationId":"decisions_v1_decisions_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kind"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Decisions V1 Decisions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/outcomes":{"get":{"tags":["Intel"],"summary":"Outcomes","description":"What happened AFTER each executed proposal: the before snapshot taken\nat execution time vs live last-7d numbers now. No platform API links a\ndecision to its result — this does. Costs one Meta call per row, so\nlimit is capped at 10.","operationId":"outcomes_v1_outcomes_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","default":"","title":"Brand"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Outcomes V1 Outcomes Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alerts":{"get":{"tags":["Intel"],"summary":"Alerts","description":"What needs attention right now, ranked: delivery issues, fatigue,\nunderperformance vs the account's own benchmarks, budget pacing blowouts.\nPoll this instead of re-deriving anomalies from raw metrics.","operationId":"alerts_v1_alerts_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Alerts V1 Alerts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/performance/unified":{"get":{"tags":["Intel"],"summary":"Performance Unified","description":"Every channel in one view — ads, Threads, Telegram, newsletter — plus\nthe draft pipeline. No single platform API has this; use it to decide\nWHERE to spend effort before drilling into a channel.","operationId":"performance_unified_v1_performance_unified_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Performance Unified V1 Performance Unified Get"}}}}}}},"/v1/policies":{"get":{"tags":["Intel"],"summary":"Get Policies","description":"The brand's guardrails + standing auto-approve rules. READ THIS BEFORE\nPROPOSING: stay under the caps and you stop wasting proposals; kinds in\nauto_approve execute without waiting for the user (their standing\nconsent, set in the dashboard).","operationId":"get_policies_v1_policies_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Policies V1 Policies Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/media/content-upload":{"post":{"tags":["Ads","Content"],"summary":"Upload Content Image","description":"Upload one image for a Threads root or reply.\n\nReturns a stable public ``image_url``. Put one or more returned URLs in a\nThreads draft's ``image_urls`` (root post) or the matching entry of\n``chain_image_urls`` (self-reply). Files are stored in DigitalOcean Spaces,\nnever in the project checkout.","operationId":"upload_content_image_v1_media_content_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_content_image_v1_media_content_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Upload Content Image V1 Media Content Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/media/poster":{"post":{"tags":["Ads"],"summary":"Poster","description":"Generate an ad poster. Pass `brand` to upload it to that brand's Meta ad\naccount — the returned image_hash plugs straight into a meta_ad draft.\n\nEvery poster is also archived to durable storage (DigitalOcean Spaces) the\nmoment it's generated, so it survives a failed Meta upload or a dropped\nresponse. The reply carries `archive_key` (stable pointer) and `archive_url`\n(presigned, fetchable for ~7 days) — reference the stored image later\nwithout regenerating.\n\nImage generation is included in the subscription and metered per plan —\nthe platform supplies the AI keys (`simple` -> gemini, `complex` -> openai).\nEach image model's gateway is set on its row in the Models tab (Route:\ndirect, or via the Pareto gateway pareto.my); the caller doesn't choose the\ngateway here.","operationId":"poster_v1_media_poster_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PosterIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Poster V1 Media Poster Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/media/upload":{"post":{"tags":["Ads"],"summary":"Upload","description":"Upload your OWN image or video as an ad creative instead of AI-generating\none.\n\nThe bring-your-own-creative counterpart of POST /media/poster: uploads the\nfile to `brand`'s Meta ad account and returns a creative reference that plugs\nstraight into a meta_ad draft. The file type is auto-detected:\n\n- **Image** (PNG / JPEG / GIF / WEBP, ≤30MB) → `{image_hash, image_url}`.\n- **Video** (MP4 / MOV, ≤200MB) → `{video_id, image_url, video_url}`. The\n  call waits for Meta to finish processing the video, then returns its\n  `video_id` plus an auto-generated `image_url` thumbnail (the poster frame)\n  and a playable `video_url`. Put `video_id` (and the thumbnail `image_url`)\n  on the meta_ad draft.\n\nLike a generated poster, the creative is archived to durable storage\n(DigitalOcean Spaces) — the reply carries `archive_key` / `archive_url`.\n\nMultipart form: `brand` (field) + `file` (the image or video). No AI\nprovider key needed — this path never calls a model, so it works without a\nProviders-tab key.","operationId":"upload_v1_media_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_v1_media_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Upload V1 Media Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts":{"get":{"tags":["Ads"],"summary":"Accounts","description":"Connected brands. A brand without page_id is read-only (no ad push).\n\ngoogle_can_push mirrors can_push for Google: the row has a\ngoogle_customer_id AND this workspace has usable Google credentials\n(OAuth app configured plus a stored connection — or, for the default\nworkspace, the env refresh token).","operationId":"accounts_v1_accounts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Accounts V1 Accounts Get"}}}}}}},"/v1/accounts/health":{"get":{"tags":["Ads"],"summary":"Account Health","description":"LIVE delivery health for one brand's ad account: account_status,\ncan_spend, disable_reason (names a card/billing problem when that's the\ncause) and today's account spend. This is the 'is my ad actually running?'\ncheck — use it before assuming ads are delivering.","operationId":"account_health_v1_accounts_health_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Account Health V1 Accounts Health Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sales/brands":{"get":{"tags":["Sales"],"summary":"Sales Brands","description":"The sales-tracker brand registry. Call this to discover which `brand`\nvalues /sales/summary and /sales/daily accept — covers base brands\n(pandaitech, limaulabs, hundrads) and product views (limaulabs_pai, ...).","operationId":"sales_brands_v1_sales_brands_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sales Brands V1 Sales Brands Get"}}}}}}},"/v1/sales/summary":{"get":{"tags":["Sales"],"summary":"Sales Tracker Summary","description":"Sales + ad-spend KPIs for a brand over [start_date, end_date]\n(YYYY-MM-DD), with a prior-period comparison: sales, orders, ad_cost,\nprofit, roas, aov, cac, margin.","operationId":"sales_tracker_summary_v1_sales_summary_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sales Tracker Summary V1 Sales Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sales/daily":{"get":{"tags":["Sales"],"summary":"Sales Tracker Daily","description":"Per-day sales + ad spend breakdown for a brand (newest day first),\nincluding per-package order counts.","operationId":"sales_tracker_daily_v1_sales_daily_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sales Tracker Daily V1 Sales Daily Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/summary":{"get":{"tags":["Analytics"],"summary":"Analytics Summary","description":"GA4 KPI totals (sessions, users, page views, key events, revenue,\nengagement) for a brand over [start_date, end_date] (YYYY-MM-DD), with a\nprior same-length-period comparison.","operationId":"analytics_summary_v1_analytics_summary_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Summary V1 Analytics Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/daily":{"get":{"tags":["Analytics"],"summary":"Analytics Daily","description":"Per-day GA4 KPI breakdown for a brand (newest day first).","operationId":"analytics_daily_v1_analytics_daily_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Daily V1 Analytics Daily Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/channels":{"get":{"tags":["Analytics"],"summary":"Analytics Channels","description":"GA4 traffic by default channel group (Paid Social, Organic Search, …) —\nsessions/users/key events/revenue per channel, busiest first.","operationId":"analytics_channels_v1_analytics_channels_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Channels V1 Analytics Channels Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/pages":{"get":{"tags":["Analytics"],"summary":"Analytics Pages","description":"Top pages by views for a brand over the window.","operationId":"analytics_pages_v1_analytics_pages_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","title":"End Date"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Pages V1 Analytics Pages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brand/brief":{"get":{"tags":["Brand"],"summary":"Brand Brief","description":"The brand's full knowledge brief — voice, USP, audience, offers,\nchannel playbooks. ALWAYS read this before drafting ads or content for\na brand; pair it with the library endpoints (what performed) to ground\nboth WHAT the brand is and WHAT worked.","operationId":"brand_brief_v1_brand_brief_get","parameters":[{"name":"brand","in":"query","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"per_asset_chars","in":"query","required":false,"schema":{"type":"integer","default":8000,"title":"Per Asset Chars"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Brand Brief V1 Brand Brief Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brand/notes":{"post":{"tags":["Brand"],"summary":"Add Brand Note","description":"Save a text note into a brand's knowledge base (shows up in the\nbrief). Use when you learn something durable mid-task — a positioning\ninsight, an audience fact, an offer detail the user confirmed — so the\nnext drafting session starts smarter.","operationId":"add_brand_note_v1_brand_notes_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandNoteIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Add Brand Note V1 Brand Notes Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/models":{"get":{"tags":["AI"],"summary":"List Models","description":"The AI model registry — name, provider, model_id, what each is good\nat, and status (current/verify/legacy). ALWAYS read this before picking\na model for /v1/complete: training data is stale, this registry is\nmaintained.\n\nEach model carries a derived `providers` list — every provider that serves\nit (its primary `provider` plus any `provider_variants`). Pass the model's\n`model_id` and any of those providers to /v1/complete; Hundrads maps to the\nprovider-specific id.","operationId":"list_models_v1_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Models V1 Models Get"}}}}}}},"/v1/complete":{"post":{"tags":["AI"],"summary":"Complete","description":"Run a prompt through a provider/model and return the completion(s).\n\nThe model must exist in the registry (GET /v1/models) for that provider.\nPass `skill` / `channel` / `brand` so the call's cost is attributed in\nthe api_calls log.","operationId":"complete_v1_complete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Complete V1 Complete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reports/ads":{"post":{"tags":["Intel"],"summary":"Send Ads Report","description":"Send the end-of-run ads report. Call EXACTLY ONCE per brand at the end\nof a successful management run. The report is a marketer's stand-up, not\na data dump: headline = the outcome in one sentence; actions = proposals\nfiled this run; watching/outcomes/blocked = short plain-English bullets;\nnext_step = one line with budget impact. Never mention the approval flow,\ninternal ids, or tool names.","operationId":"send_ads_report_v1_reports_ads_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdsReportIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Send Ads Report V1 Reports Ads Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reports/ad-preview":{"post":{"tags":["Intel"],"summary":"Send Ad Preview","description":"Send ONE message previewing a single new test ad — a rendered mockup of\nhow the creative looks in a Facebook/Instagram feed, plus the copy + budget.\nCall once per ad the autopilot staged this pass (the per-brand digest still\ngoes once, via POST /reports/ads). Delivery resolves to the same channel +\nwebhook the user saved in the dashboard Reports tab; the agent never holds\nthe webhook.","operationId":"send_ad_preview_v1_reports_ad_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdPreviewIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Send Ad Preview V1 Reports Ad Preview Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AdEditPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"ad_id":{"type":"string","maxLength":40,"minLength":1,"title":"Ad Id"},"ad_name":{"type":"string","maxLength":300,"title":"Ad Name","default":""},"primary_text":{"type":"string","maxLength":3000,"minLength":10,"title":"Primary Text"},"headline":{"type":"string","maxLength":255,"minLength":2,"title":"Headline"},"description":{"type":"string","maxLength":255,"title":"Description","default":""},"link_url":{"type":"string","maxLength":1000,"minLength":8,"title":"Link Url"},"call_to_action":{"type":"string","title":"Call To Action","default":"LEARN_MORE"},"image_hash":{"type":"string","title":"Image Hash","default":""},"image_url":{"type":"string","title":"Image Url","default":""},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"}},"type":"object","required":["brand","ad_id","primary_text","headline","link_url","reason"],"title":"AdEditPayload","description":"Propose new copy for a live ad (approve swaps in a fresh creative)."},"AdPreviewIn":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"ad_name":{"type":"string","minLength":1,"title":"Ad Name","description":"the variant's ad name"},"primary_text":{"type":"string","title":"Primary Text","default":""},"headline":{"type":"string","title":"Headline","default":""},"description":{"type":"string","title":"Description","default":""},"call_to_action":{"type":"string","title":"Call To Action","default":""},"link_url":{"type":"string","title":"Link Url","default":""},"image_url":{"type":"string","title":"Image Url","description":"creative to render (poster/upload image_url)","default":""},"page_name":{"type":"string","title":"Page Name","description":"display name; defaults to the brand","default":""},"campaign_name":{"type":"string","title":"Campaign Name","default":""},"daily_budget_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Budget Cents"},"agent_note":{"type":"string","title":"Agent Note","description":"one line: what this variant tests","default":""}},"type":"object","required":["brand","ad_name"],"title":"AdPreviewIn"},"AdsReportIn":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"kind":{"type":"string","title":"Kind","description":"daily = the once-a-day digest (server renders the live numbers block); alert = 'needs you' ping (@here on Discord) — send ONLY when something needs the operator","default":"daily"},"headline":{"type":"string","minLength":1,"title":"Headline","description":"ONE sentence, the outcome"},"health":{"additionalProperties":true,"type":"object","title":"Health","description":"only facts the dashboard images can't show"},"actions":{"items":{},"type":"array","title":"Actions","description":"one entry per proposal filed this run: {kind, object_name, summary, status}"},"watching":{"items":{},"type":"array","title":"Watching"},"outcomes":{"items":{},"type":"array","title":"Outcomes"},"recommendations":{"items":{},"type":"array","title":"Recommendations","description":"proactive suggestions: specific things the agent wants to try next and why"},"blocked":{"items":{},"type":"array","title":"Blocked"},"next_step":{"type":"string","title":"Next Step","default":""},"creative_url":{"type":"string","title":"Creative Url","default":""},"fingerprint":{"type":"string","title":"Fingerprint","description":"hash of the current situation; report sends when it changes","default":""},"min_interval_min":{"type":"integer","title":"Min Interval Min","description":"min minutes between unchanged reports (0 = always send)","default":0},"force":{"type":"boolean","title":"Force","description":"bypass dedup and send now","default":false}},"type":"object","required":["brand","headline"],"title":"AdsReportIn"},"AgentNoteIn":{"properties":{"brand":{"type":"string","title":"Brand","description":"brand this note is about (optional)","default":""},"text":{"type":"string","minLength":1,"title":"Text","description":"the learning/judgment, in plain words"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"}},"type":"object","required":["text"],"title":"AgentNoteIn"},"Body_upload_content_image_v1_media_content_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_content_image_v1_media_content_upload_post"},"Body_upload_v1_media_upload_post":{"properties":{"brand":{"type":"string","title":"Brand"},"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["brand","file"],"title":"Body_upload_v1_media_upload_post"},"BrandNoteIn":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"title":{"type":"string","maxLength":200,"title":"Title","default":""},"content":{"type":"string","minLength":1,"title":"Content"}},"type":"object","required":["brand","content"],"title":"BrandNoteIn"},"BudgetChangePayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"adset_id":{"type":"string","maxLength":40,"minLength":1,"title":"Adset Id"},"adset_name":{"type":"string","maxLength":300,"title":"Adset Name","default":""},"current_daily_budget_cents":{"type":"integer","minimum":0.0,"title":"Current Daily Budget Cents","default":0},"new_daily_budget_cents":{"type":"integer","maximum":100000.0,"minimum":100.0,"title":"New Daily Budget Cents"},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"},"expected_impact":{"type":"string","maxLength":1000,"title":"Expected Impact","default":""}},"type":"object","required":["brand","adset_id","new_daily_budget_cents","reason"],"title":"BudgetChangePayload","description":"Propose a new daily budget for a live ad set."},"CommentHidePayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"comment_id":{"type":"string","maxLength":80,"minLength":1,"title":"Comment Id"},"comment_text":{"type":"string","maxLength":2000,"title":"Comment Text","default":""},"commenter":{"type":"string","maxLength":200,"title":"Commenter","default":""},"reason":{"type":"string","maxLength":1000,"minLength":5,"title":"Reason"}},"type":"object","required":["brand","comment_id","reason"],"title":"CommentHidePayload","description":"Propose hiding a comment (spam/abuse) on one of the brand's posts/ads."},"CommentReactPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"comment_id":{"type":"string","maxLength":80,"minLength":1,"title":"Comment Id"},"comment_text":{"type":"string","maxLength":2000,"title":"Comment Text","default":""},"commenter":{"type":"string","maxLength":200,"title":"Commenter","default":""},"reason":{"type":"string","maxLength":1000,"title":"Reason","default":""}},"type":"object","required":["brand","comment_id"],"title":"CommentReactPayload","description":"Propose liking a comment (page reaction) on one of the brand's posts/ads."},"CommentReplyPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"comment_id":{"type":"string","maxLength":80,"minLength":1,"title":"Comment Id"},"message":{"type":"string","maxLength":2000,"minLength":1,"title":"Message"},"comment_text":{"type":"string","maxLength":2000,"title":"Comment Text","default":""},"commenter":{"type":"string","maxLength":200,"title":"Commenter","default":""},"post_id":{"type":"string","maxLength":80,"title":"Post Id","default":""},"reason":{"type":"string","maxLength":1000,"title":"Reason","default":""}},"type":"object","required":["brand","comment_id","message"],"title":"CommentReplyPayload","description":"Propose a public reply to a comment on one of the brand's posts/ads."},"CompleteIn":{"properties":{"prompt":{"type":"string","minLength":1,"title":"Prompt"},"system":{"type":"string","title":"System","default":""},"provider":{"type":"string","title":"Provider","default":"claude"},"model":{"type":"string","minLength":1,"title":"Model"},"n":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"N","default":1},"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","default":0.8},"skill":{"type":"string","title":"Skill","description":"which skill triggered this call","default":""},"channel":{"type":"string","title":"Channel","description":"telegram|threads|newsletter|ads|sales","default":""},"brand":{"type":"string","title":"Brand","default":""}},"type":"object","required":["prompt","model"],"title":"CompleteIn"},"CreativeImage":{"properties":{"image_hash":{"type":"string","minLength":1,"title":"Image Hash"},"image_url":{"type":"string","title":"Image Url","default":""},"ratio":{"type":"string","enum":["1:1","4:5","9:16","16:9"],"title":"Ratio","default":"1:1"}},"type":"object","required":["image_hash"],"title":"CreativeImage","description":"One ratio variant of an ad creative — an image_hash plus the aspect it\nwas composed at. A meta_ad draft carrying two or more of these (distinct\nratios) is pushed as a single placement-customized creative (Meta serves\nthe right crop per placement — feed gets the square/portrait, Stories/Reels\nget the 9:16); see meta.create_paused_ad / _build_asset_feed_spec."},"DraftIn":{"properties":{"kind":{"type":"string","enum":["meta_ad","google_search_ad","threads_post","facebook_post","instagram_post","linkedin_post","telegram_post","newsletter","budget_change","status_change","ad_edit","comment_reply","comment_hide","comment_react","google_budget_change","google_status_change","google_negative_keyword"],"title":"Kind"},"payload":{"anyOf":[{"$ref":"#/components/schemas/MetaAdPayload"},{"$ref":"#/components/schemas/GoogleSearchAdPayload"},{"$ref":"#/components/schemas/ThreadsPostPayload"},{"$ref":"#/components/schemas/FacebookPostPayload"},{"$ref":"#/components/schemas/InstagramPostPayload"},{"$ref":"#/components/schemas/LinkedInPostPayload"},{"$ref":"#/components/schemas/TelegramPostPayload"},{"$ref":"#/components/schemas/NewsletterPayload"},{"$ref":"#/components/schemas/BudgetChangePayload"},{"$ref":"#/components/schemas/StatusChangePayload"},{"$ref":"#/components/schemas/AdEditPayload"},{"$ref":"#/components/schemas/CommentReplyPayload"},{"$ref":"#/components/schemas/CommentHidePayload"},{"$ref":"#/components/schemas/CommentReactPayload"},{"$ref":"#/components/schemas/GoogleBudgetChangePayload"},{"$ref":"#/components/schemas/GoogleStatusChangePayload"},{"$ref":"#/components/schemas/GoogleNegativeKeywordPayload"}],"title":"Payload"},"agent_note":{"type":"string","maxLength":2000,"title":"Agent Note","default":""},"batch_id":{"type":"string","maxLength":64,"title":"Batch Id","default":""}},"type":"object","required":["kind","payload"],"title":"DraftIn","description":"POST /v1/drafts body. `kind` routes payload validation per channel."},"DraftUpdateIn":{"properties":{"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"agent_note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Agent Note"}},"type":"object","title":"DraftUpdateIn","description":"PATCH /v1/drafts/{id} body.\n\nPayload fields are merged into the stored payload and then the complete\nresult is validated against the draft's existing kind. The kind itself is\nimmutable: changing the kind means withdrawing and creating a new draft."},"FacebookPostPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"text":{"type":"string","maxLength":63206,"minLength":1,"title":"Text"},"image_url":{"type":"string","maxLength":2000,"title":"Image Url","default":""},"link_url":{"type":"string","maxLength":2000,"title":"Link Url","default":""},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["brand","text","scheduled_at"],"title":"FacebookPostPayload","description":"One organic post for a registered brand's Facebook Page."},"GoogleBudgetChangePayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"campaign_id":{"type":"string","maxLength":40,"minLength":1,"title":"Campaign Id"},"campaign_name":{"type":"string","maxLength":300,"title":"Campaign Name","default":""},"current_daily_budget_cents":{"type":"integer","minimum":0.0,"title":"Current Daily Budget Cents","default":0},"new_daily_budget_cents":{"type":"integer","maximum":100000.0,"minimum":100.0,"title":"New Daily Budget Cents"},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"},"expected_impact":{"type":"string","maxLength":1000,"title":"Expected Impact","default":""}},"type":"object","required":["brand","campaign_id","new_daily_budget_cents","reason"],"title":"GoogleBudgetChangePayload","description":"Propose a new daily budget for a live Google Search campaign."},"GoogleKeyword":{"properties":{"text":{"type":"string","maxLength":80,"minLength":1,"title":"Text"},"match_type":{"type":"string","enum":["BROAD","PHRASE","EXACT"],"title":"Match Type","default":"PHRASE"}},"type":"object","required":["text"],"title":"GoogleKeyword","description":"One Search keyword — text plus match type. The same shape serves both\npositive and negative keyword lists."},"GoogleNegativeKeywordPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"campaign_id":{"type":"string","maxLength":40,"minLength":1,"title":"Campaign Id"},"campaign_name":{"type":"string","maxLength":300,"title":"Campaign Name","default":""},"keywords":{"items":{"$ref":"#/components/schemas/GoogleKeyword"},"type":"array","maxItems":100,"minItems":1,"title":"Keywords"},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"}},"type":"object","required":["brand","campaign_id","keywords","reason"],"title":"GoogleNegativeKeywordPayload","description":"Propose campaign-level negative keywords (cut the waste the search-terms\nreport surfaces — see GET /v1/search-terms)."},"GoogleSearchAdPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"campaign_name":{"type":"string","maxLength":200,"minLength":3,"title":"Campaign Name"},"ad_group_name":{"type":"string","maxLength":200,"minLength":3,"title":"Ad Group Name"},"headlines":{"items":{"type":"string"},"type":"array","maxItems":15,"minItems":3,"title":"Headlines"},"descriptions":{"items":{"type":"string"},"type":"array","maxItems":4,"minItems":2,"title":"Descriptions"},"path1":{"type":"string","maxLength":15,"title":"Path1","default":""},"path2":{"type":"string","maxLength":15,"title":"Path2","default":""},"final_url":{"type":"string","maxLength":1000,"minLength":8,"title":"Final Url"},"keywords":{"items":{"$ref":"#/components/schemas/GoogleKeyword"},"type":"array","maxItems":50,"minItems":1,"title":"Keywords"},"negative_keywords":{"items":{"$ref":"#/components/schemas/GoogleKeyword"},"type":"array","title":"Negative Keywords"},"daily_budget_cents":{"type":"integer","maximum":100000.0,"minimum":100.0,"title":"Daily Budget Cents"},"geo_countries":{"items":{"type":"string"},"type":"array","title":"Geo Countries"},"bidding_strategy":{"type":"string","enum":["MAXIMIZE_CLICKS","MAXIMIZE_CONVERSIONS"],"title":"Bidding Strategy","default":"MAXIMIZE_CLICKS"}},"type":"object","required":["brand","campaign_name","ad_group_name","headlines","descriptions","final_url","keywords","daily_budget_cents"],"title":"GoogleSearchAdPayload","description":"The spec for one Google Search ad draft — everything approve needs to\npush the chain (budget → campaign → ad group → RSA + keywords) PAUSED."},"GoogleStatusChangePayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"object_type":{"type":"string","enum":["campaign","ad_group","ad"],"title":"Object Type"},"object_id":{"type":"string","maxLength":40,"minLength":1,"title":"Object Id"},"object_name":{"type":"string","maxLength":300,"title":"Object Name","default":""},"action":{"type":"string","enum":["pause","resume"],"title":"Action"},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"}},"type":"object","required":["brand","object_type","object_id","action","reason"],"title":"GoogleStatusChangePayload","description":"Propose pausing or resuming a live Google campaign / ad group / ad."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InstagramPostPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"caption":{"type":"string","maxLength":2200,"minLength":1,"title":"Caption"},"image_url":{"type":"string","maxLength":2000,"title":"Image Url","default":""},"image_urls":{"items":{"type":"string"},"type":"array","maxItems":10,"title":"Image Urls"},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["brand","caption","scheduled_at"],"title":"InstagramPostPayload","description":"One image-feed post or image carousel for a brand's Instagram account."},"LinkedInPostPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"text":{"type":"string","maxLength":3000,"minLength":1,"title":"Text"},"image_url":{"type":"string","maxLength":2000,"title":"Image Url","default":""},"link_url":{"type":"string","maxLength":2000,"title":"Link Url","default":""},"link_title":{"type":"string","maxLength":200,"title":"Link Title","default":""},"link_description":{"type":"string","maxLength":256,"title":"Link Description","default":""},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["brand","text","scheduled_at"],"title":"LinkedInPostPayload","description":"One LinkedIn profile or organization Page post."},"MetaAdPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"campaign_name":{"type":"string","maxLength":200,"minLength":3,"title":"Campaign Name"},"ad_name":{"type":"string","maxLength":200,"minLength":3,"title":"Ad Name"},"objective":{"type":"string","title":"Objective","default":"OUTCOME_TRAFFIC"},"primary_text":{"type":"string","maxLength":3000,"minLength":10,"title":"Primary Text"},"headline":{"type":"string","maxLength":255,"minLength":2,"title":"Headline"},"description":{"type":"string","maxLength":255,"title":"Description","default":""},"call_to_action":{"type":"string","title":"Call To Action","default":"LEARN_MORE"},"destination":{"type":"string","enum":["website","whatsapp"],"title":"Destination","default":"website"},"whatsapp_prefill":{"type":"string","maxLength":500,"title":"Whatsapp Prefill","default":""},"link_url":{"type":"string","maxLength":1000,"title":"Link Url","default":""},"daily_budget_cents":{"type":"integer","maximum":100000.0,"minimum":100.0,"title":"Daily Budget Cents"},"image_hash":{"type":"string","title":"Image Hash","default":""},"image_url":{"type":"string","title":"Image Url","default":""},"video_id":{"type":"string","title":"Video Id","default":""},"video_url":{"type":"string","title":"Video Url","default":""},"images":{"items":{"$ref":"#/components/schemas/CreativeImage"},"type":"array","maxItems":4,"title":"Images"},"geo_countries":{"items":{"type":"string"},"type":"array","title":"Geo Countries"}},"type":"object","required":["brand","campaign_name","ad_name","primary_text","headline","daily_budget_cents"],"title":"MetaAdPayload","description":"The spec for one Meta ad draft — everything approve needs to push."},"NewsletterPayload":{"properties":{"subject":{"type":"string","maxLength":200,"minLength":3,"title":"Subject"},"body":{"type":"string","maxLength":50000,"minLength":50,"title":"Body"},"preview_text":{"type":"string","maxLength":300,"title":"Preview Text","default":""},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["subject","body","scheduled_at"],"title":"NewsletterPayload","description":"One email newsletter draft. Body is markdown; on approval the schedule\nloop converts it to simple HTML and hands it to Kit at the slot time."},"NotifyCheckIn":{"properties":{"scope":{"type":"string","minLength":1,"title":"Scope","description":"e.g. 'digest', 'alert', 'daily-summary'"},"key":{"type":"string","minLength":1,"title":"Key","description":"dedup key, e.g. brand or '<brand>:roas-crash'"},"fingerprint":{"type":"string","title":"Fingerprint","description":"hash of the CURRENT situation; send when it changes","default":""},"min_interval_min":{"type":"integer","title":"Min Interval Min","description":"min minutes between sends of an unchanged item","default":0},"force":{"type":"boolean","title":"Force","default":false}},"type":"object","required":["scope","key"],"title":"NotifyCheckIn"},"PosterIn":{"properties":{"prompt":{"type":"string","maxLength":4000,"minLength":10,"title":"Prompt"},"complexity":{"type":"string","enum":["simple","complex"],"title":"Complexity","default":"simple"},"aspect":{"type":"string","enum":["1:1","4:5","9:16","16:9"],"title":"Aspect","default":"1:1"},"brand":{"type":"string","title":"Brand","default":""},"quality":{"type":"string","enum":["low","medium","high"],"title":"Quality","default":"high"},"include_b64":{"type":"boolean","title":"Include B64","default":false},"reference_images":{"items":{"type":"string"},"type":"array","maxItems":4,"title":"Reference Images"}},"type":"object","required":["prompt"],"title":"PosterIn","description":"POST /v1/media/poster body — wraps image generation."},"RefreshIn":{"properties":{"target":{"type":"string","title":"Target","description":"ads | posts | threads | broadcasts"},"brand":{"type":"string","title":"Brand","description":"ads only — one brand, empty = all","default":""},"platform":{"type":"string","title":"Platform","description":"ads only — meta (default) | google","default":"meta"},"full":{"type":"boolean","title":"Full","description":"full resync instead of incremental","default":false}},"type":"object","required":["target"],"title":"RefreshIn"},"StatusChangePayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"object_type":{"type":"string","enum":["ad","adset","campaign"],"title":"Object Type"},"object_id":{"type":"string","maxLength":40,"minLength":1,"title":"Object Id"},"object_name":{"type":"string","maxLength":300,"title":"Object Name","default":""},"action":{"type":"string","enum":["pause","resume"],"title":"Action"},"reason":{"type":"string","maxLength":2000,"minLength":10,"title":"Reason"}},"type":"object","required":["brand","object_type","object_id","action","reason"],"title":"StatusChangePayload","description":"Propose pausing or resuming a live ad / ad set / campaign."},"TelegramPostPayload":{"properties":{"brand":{"type":"string","minLength":1,"title":"Brand"},"text":{"type":"string","maxLength":4096,"minLength":1,"title":"Text"},"image_url":{"type":"string","title":"Image Url","default":""},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["brand","text","scheduled_at"],"title":"TelegramPostPayload","description":"One Telegram channel post for a registered brand."},"ThreadsPostPayload":{"properties":{"text":{"type":"string","maxLength":500,"minLength":1,"title":"Text"},"chain":{"items":{"type":"string"},"type":"array","maxItems":9,"title":"Chain"},"image_urls":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Image Urls"},"chain_image_urls":{"items":{"items":{"type":"string"},"type":"array"},"type":"array","maxItems":9,"title":"Chain Image Urls"},"account":{"type":"string","maxLength":100,"title":"Account","default":""},"scheduled_at":{"type":"string","title":"Scheduled At"}},"type":"object","required":["text","scheduled_at"],"title":"ThreadsPostPayload","description":"One Threads post draft with optional media on the root or replies."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"Drafts","description":"Submit work and read verdicts. POST creates a draft; PATCH revises a pending draft; DELETE withdraws it. GET lists drafts with `status`, `review_note` (why the user rejected — apply it before redrafting) and `published_refs`."},{"name":"Content","description":"The content schedule. Call `GET /v1/schedule` BEFORE proposing a `scheduled_at` on a content draft — pick a free slot, leave hours between posts on the same channel."},{"name":"Ads","description":"Research + creative for ads: connected brands, the live ads view (GET /v1/ads — the ids to propose against), the account's own past-ad performance library, live Meta insights with breakdowns + compare, budget pacing, and poster generation. Study before drafting or proposing."},{"name":"Engagement","description":"Comments (with `answered` flags), audiences and leads. Acting on a comment is a draft like everything else: POST /v1/drafts kind `comment_reply` / `comment_hide`."},{"name":"Intel","description":"What a raw platform API can't tell you: the account's own benchmarks, the user's decision history with reasons, before/after outcomes of executed proposals, ranked alerts, every channel in one performance view, and the brand's policies (caps + standing auto-approve rules). Read policies and decisions BEFORE proposing."}]}