# AutoMonitor External Agent API Base: https://auto-monitor-sit.ambr.io. No public MCP. ## Authentication Use an Account-bound user PAT: ```http Authorization: Bearer mia_pat_... Content-Type: application/json ``` Account IDs are opaque. Reuse it. Keep credentials out of URLs/reports/logs, use identity-selector headers or bypass 403/404 by switching identity. Errors: top-level `detail`: - **400**: invalid request or unavailable operation. - **401**: missing or malformed PAT. - **402**: insufficient MIA Points. - **403**: Platform or Product policy denies the current identity. - **404**: the resource does not exist in the selected Account. - **409**: disabled operation or conflicting idempotency-key reuse. - **422**: invalid request shape, field, range, or Skill type. - **503**: temporary dependency failure. Retry reads with bounded backoff; check current state before retrying a write. ## Workflow 1. Confirm Account, subject, platforms, language, exclusions and cadence. 2. Read `/skills/format` and `/monitors/format`; select/create a Skill and monitor. 3. Verify saved tags; execute requested actions, poll tasks and return IDs/results/URLs. If tag operations report `没账号` or `no account`, the monitor owner must complete AutoData Web SSO once before retrying. ## REST routes PATs also work on Account-scoped `/api/v1` APIs. Read `/api/external/v1/schema` for methods/fields (`?prefix=/api/v1/posts` filters). Admin APIs require the admin Skill. Use `monitor_group_id`, not `monitor_id`; all routes enforce scope. ```http GET /api/external/v1/schema GET /api/external/v1/skills/format POST /api/external/v1/skills GET /api/external/v1/skills GET /api/external/v1/skills/{skill_id} PATCH /api/external/v1/skills/{skill_id} DELETE /api/external/v1/skills/{skill_id} GET /api/external/v1/monitors/format POST /api/external/v1/monitors GET /api/external/v1/monitors GET /api/external/v1/monitors/ids GET /api/external/v1/monitors/{monitor_group_id} PATCH /api/external/v1/monitors/{monitor_group_id} DELETE /api/external/v1/monitors/{monitor_group_id} GET /api/external/v1/monitors/{monitor_group_id}/tags PUT /api/external/v1/monitors/{monitor_group_id}/tags/{key} POST /api/external/v1/monitors/{monitor_group_id}/sync-autodata GET /api/external/v1/monitors/{monitor_group_id}/posts GET /api/external/v1/monitors/{monitor_group_id}/reports GET /api/external/v1/monitors/{monitor_group_id}/topics GET /api/external/v1/threads/probe/format POST /api/external/v1/threads/probe POST /api/external/v1/monitors/{monitor_group_id}/crawl POST /api/external/v1/monitors/{monitor_group_id}/analyze POST /api/external/v1/monitors/{monitor_group_id}/topics POST /api/external/v1/monitors/{monitor_group_id}/report GET /api/external/v1/tasks/{task_id} GET /api/external/v1/monitors/data/tools GET /api/external/v1/monitors/{monitor_group_id}/data/posts GET /api/external/v1/monitors/{monitor_group_id}/data/topics GET /api/external/v1/monitors/{monitor_group_id}/data/reports GET /api/external/v1/monitors/{monitor_group_id}/data/trends/sentiment GET /api/external/v1/monitors/{monitor_group_id}/data/trends/daily-volume GET /api/external/v1/monitors/{monitor_group_id}/data/trends/time-compare GET /api/external/v1/monitors/{monitor_group_id}/data/trends/sentiment-compare GET /api/external/v1/monitors/{monitor_group_id}/data/trends/period-compare ``` `POST /threads/probe` returns **501** before creating a task; do not use it. `/analyze` returns 410; use `/topics` or `/report`. ## Report Skills Skills write reports only. Fields: `GET /api/external/v1/skills/format`. Use `type=report`; legacy `report_writing` works. `topic` returns **422** `topic_skill_retired`; other ineligible types return `report_skill_type_invalid`. Require `name`, `content`, and an output. Bind only `ready` Skills. Runs yield HTML + text from one snapshot; Skill summary rules override defaults. Delivery appends the report link. `config.engine` is metadata; runtime: OpenAI Agents SDK/Responses. Delete only on request. Bound Skills return **409** `skill_in_use`; unbind first. ## Monitors and tags Fields: `GET /api/external/v1/monitors/format`. Weibo sort: `latest`, `hot`, or `all`. Douyin defaults to `latest`; requests carry publication windows. - `relevance_criteria` / `sentiment_criteria`: optional, ≥8 chars each. Supply both to avoid LLM optimization and MIA Points charges; omitted values get defaults and may optimize while paused. - `keywords` are seeds; `data_requirements` states scope and exclusions. - Facebook accepts pages or keywords; keyword-only monitors need no page. Unchanged metrics/comments reuse detail. - Instagram channel windows replace legacy `date_posted` filters. - Threads max **100** posts; every other platform max **50** (clamped). - `platform_crawl_crons` minimum **30 minutes** (faster: **400**). Keyword caps: Admin Settings (default <3h:10, ≥3h:15). - Admin sets XHS ratio (default 0/1 RedNote); manual/urgent runs unchanged. - Pausing stops schedules; manual runs do not resume them. - `urgent_crawl_enabled`: optional boolean, default **false**. Enabling it requires a valid one-time Beijing-time window. - Urgent-window `report_enabled`: optional boolean, default **false**. When true, the ordinary report schedule pauses for that window and resumes afterward. Urgent collection and reports consume more MIA Points. - Topics use shortest cron; gaps are partial. Collection ends after ingestion; analysis is separate. Honor 429 Retry-After; retain remote IDs. - Topic lookback accepts 6, 24, 72, 168, or null; granularity accepts empty, `coarse`, or `fine`. - Skills share sources; plans prepare labels/code/templates. Valid updates replace frozen versions; failed/pending updates keep the old version. Required classification runs before saving; retain unknown coverage, never zero. Labels offers backfill, future-only or ignore; no writes before choice. Historical gaps preserve future-only consent. - `reports[].delivery_mode`: `always` (default), `alert_only`, or `store_only`. Reports generate and persist; only delivery is conditional. Skill `config.alert_rules` defines urgency; unknown assessments do not send alerts. - `reports[].skill_id` selects a Skill. Missing/empty `reports` enables the daily default. A disabled-only array collects without reports; the default Skill is copied into the current Account. - `analyses` accepts `topic` and `report`. Omission enables both; an explicit empty array makes the monitor collection-only. ## ReportPlan v1 compatibility `report-plan/v1` deprecated aliases (owner `vancyliu-design`): `report_skill_id`, `schedule.report_cron`, `skill_ids.report`, `report_channel`. Write `reports[].skill_id` / `reports[].report_channels[]`. Removal requires zero reader/write signals for seven consecutive days and every longer enabled cron. `PATCH /monitors/{id}` rejects unknown or non-patchable fields with **422** (`unknown_fields`, `accepted_fields`). After changing criteria, read `/tags`. `llm_config.custom_labels` must be an array (otherwise 422). Tags: GET lists `method` and `value_type`. PUT sets `method: jev|llm` or `value_type: enum|multi_enum`; omitted fields stay unchanged. `multi_enum` requires LLM and returns multiple options. System tags remain single-choice. External tags can be listed or updated, not created or deleted. `sync-autodata` imports posts already collected for the selected Account. Tag-mode lookup outages fail and retry collection; confirmed absent modes permit untagged collection. Delete a monitor only at the user's request; running work may block it. Its Agent routes then return 404; completed reports remain in Account history. ## Enterprise WhatsApp report channels Write a WhatsApp destination in `report_channels` as: ```json {"type":"whatsapp","value":"https://chat.whatsapp.com/EXAMPLE_INVITE"} ``` Use a group invite URL; reject empty/`auto`/`@g.us`/numeric values. Never write server-owned `endpoint_id` or provisioning fields. Read `GET /api/external/v1/monitors/{monitor_group_id}` under `data.reports[].report_channels[]`. Wait on `pending`; `bound` is ready; `error` needs correction or intentional retry. Readbacks: ```json {"type":"whatsapp","value":"https://chat.whatsapp.com/EXAMPLE_INVITE","invite_link":"https://chat.whatsapp.com/EXAMPLE_INVITE","endpoint_id":null,"provision_status":"pending","provision_error_code":null} ``` ```json {"type":"whatsapp","value":"https://chat.whatsapp.com/EXAMPLE_INVITE","invite_link":"https://chat.whatsapp.com/EXAMPLE_INVITE","endpoint_id":"endpoint-AmbER-42","provision_status":"bound","provision_error_code":null} ``` ```json {"type":"whatsapp","value":"https://chat.whatsapp.com/EXAMPLE_INVITE","invite_link":"https://chat.whatsapp.com/EXAMPLE_INVITE","endpoint_id":null,"provision_status":"error","provision_error_code":"beepmate_join_rejected"} ``` POST/PATCH may return **503** `whatsapp_provision_admission_failed` after saving; GET before retrying. Re-save `error` to retry; re-save `bound` to retain `endpoint_id`; change the invite to reset `pending`. Terminal codes: `beepmate_join_rejected`, `beepmate_provider_response_invalid`. ## Tasks and idempotency Crawl returns a top-level `task_id` and `data.task_ids[]`. Poll every ID in `task_ids`; inspect `failed_platforms` even after HTTP 200. HTTP 503 means no platform was accepted. Topics and reports require `Idempotency-Key`: 1–128 ASCII characters matching `^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$`. Reusing the same key for the same Account, route, and monitor returns the original task. Conflicting reuse returns 409. Polling failure never authorizes a new task. Poll every 10–30 seconds: ```http GET /api/external/v1/tasks/{task_id}?monitor_group_id={monitor_group_id} ``` Check `success` and `data.status`: `pending`/`running` are nonterminal; `completed`/`completed_empty`/`failed`/`cancelled` are terminal. Empty partial scans can succeed. Pending includes ingestion. Back off poll errors; inspect upstream failure codes. Tagging may take an hour: poll every five minutes, honor `Retry-After`, and reuse task/collection IDs. ## Results and MIA Points Reused posts remain eligible per monitor. RedNote `new` was selection. New results separate `searched`, `ingested`, `first_seen`, and `newly_published`; verify the latter two with their ID arrays. Posts: keep filters with `pagination.next_cursor` and `include_total=false`; reload page 1 for totals. Offsets >10,000 return 400. Account checks remain per page; ties use `(platform,biz_key)`. Set `include_unlabeled=true` until tagging completes. Report URLs expire; repeat GET. Trace paths require a returned UUID (invalid: 422). Discover analytics parameters with `/monitors/data/tools`; windows max at **720h (30d)** and exact instants use RFC 3339 `+08:00`; a larger value returns **422**. Unknown `publish_time` is empty; `ingested_time` is first ingestion. Customer billing uses only MIA Points. Reports may reserve up to 1,250 Points. Insufficient balance returns 402 (`insufficient_credits`): recharge at MIA Platform's Account page and retry. Past quota failures remain task warnings, not current balance claims. Optional admin email (default off) excludes admins; it is deduplicated and retried. Notification failure never replaces the report failure. Unused holds are released. Show charged Points, never fiat or private cost. ## Completion - Read Account/resource before writes and after uncertain writes. - Ask before deleting monitors/Skills. - Return IDs, terminal states, results/URLs and next actions. Report preparation failures persist as failed tasks (`report_preparation_not_ready` or `report_preparation_not_ready_before_deadline`); repair the binding before retry.