---
name: automonitor-external-api
description: "Use Account-scoped AutoMonitor /api/external/v1."
---

<!-- AUTOMONITOR_LLMS_CONTRACT -->

# 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.
