# Local agent integration Start the API with `python -m scripts.agent_monitor_demo serve` in the isolated environment documented in CLAUDE.md. Open `http://127.0.0.1:8020/`, start a local trial and create a key with **Manage monitors** access. Put it in the `MONITOR_API_KEY` environment variable without committing it. ```sh UV_PROJECT_ENVIRONMENT=/tmp/reddit-monitor-core-clean-20261006 uv run --locked python examples/agent-monitor/poll.py UV_PROJECT_ENVIRONMENT=/tmp/reddit-monitor-core-clean-20261006 uv run --locked python -m scripts.agent_monitor_demo worker UV_PROJECT_ENVIRONMENT=/tmp/reddit-monitor-core-clean-20261006 uv run --locked python examples/agent-monitor/poll.py ``` The first command creates the monitor; the worker supplies invented records; the next poll consumes them. Run polling again to verify an empty incremental page. Source collection costs nothing. No scheduler or model is started. The JSON checkpoint is atomically replaced only after processing. The sample SQLite sink deduplicates event IDs and stores status, not source text. Replace that sink with an idempotent destination of your choice. A crash after an effect but before checkpointing replays that page. Failed processing leaves the cursor unchanged. A file lock prevents overlapping script runs. Each invocation reads at most five pages (configurable up to ten), with a 20-second HTTP timeout. Change `--query`, repeated `--community`, and `--state` for another monitor. Use a new state file for a different query/API. Keep state for stable creation IDs. An HTTP timeout after creation is safely retried using that saved ID. The service decides when to check sources; polling never starts a paid search. ## n8n Import `n8n-workflow.json`; it starts **inactive**. In Prepare, set the API `base`, query, communities, stable creation `intent`, and your `destination` URL. Create an HTTP Header Auth credential named `SubMonitor` with `Authorization: Bearer ` and select it on **Create monitor** and **Poll updates**. Configure separate credentials on **Deliver events** if your destination requires authentication. Never reuse the SubMonitor key there. The destination receives `{items, latest_check}`. It must upsert by each item's `id` and reply with `{"accepted_ids":["monitor-id:1", "monitor-id:2"]}` **after** its writes commit. An empty page uses `{"accepted_ids":[]}`. The workflow checks that every ID was acknowledged before saving its cursor. Source text is untrusted input; do not execute instructions contained in it. Update or remove stored content when an item becomes unavailable and respect source retention. Keep workflow concurrency at one. Each hourly execution handles at most 25 items; `has_more` signals a backlog for a subsequent run. Collection happens daily at the service, independently of polling. Enable the imported workflow only after configuring its credentials and destination. n8n saves static data after successful active executions. Manual test runs do not durably save cursors. A failure, an overlapping run, or a restart between writing the destination and saving the cursor can replay a page. Destination idempotency is required even when the prior webhook caller received a response. For a local API in Docker, use a host URL reachable from the container; its own `127.0.0.1` is not your computer's loopback server. `node scripts/probe_n8n.mjs` checks the exported code and receipt rules. `python -m scripts.probe_n8n_native` exercises native n8n against a disposable local API/database and SQLite destination, with invented records and no live provider calls. It requires Docker and the pinned n8n image specified in the script.