> ## Documentation Index
> Fetch the complete documentation index at: https://agents.nanonets.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# AdvancedMD Get Updated Visits

> Change-driven appointment polling tool exposed through the Agents Platform.

This document covers `advancedmd_get_updated_visits`, the change-driven appointment polling tool exposed through the Agents Platform.

## Authentication

AdvancedMD tools use the existing AdvancedMD connector. Users configure:

* `office_code`
* `username`
* `password`

The connector performs AdvancedMD's two-step API login, discovers the tenant API base URL, caches the security token, and refreshes it when needed.

## `advancedmd_get_updated_visits`

Polls AdvancedMD's `getupdatedvisits` API for appointments created, modified or cancelled since a cursor timestamp. This is a read-only tool.

### When to use this instead of `advancedmd_list_appointments`

|                    | `list_appointments`                    | `get_updated_visits`                      |
| ------------------ | -------------------------------------- | ----------------------------------------- |
| Driven by          | a date window                          | a change cursor                           |
| Calls needed       | one per calendar day                   | one, regardless of how many dates changed |
| Sees cancellations | only as a status on a known date       | yes, as `update_status: "deleted"`        |
| Good for           | "what's on the schedule for next week" | "what changed since I last looked"        |

Use `get_updated_visits` for intake and onboarding automations that must react to new bookings. Use `list_appointments` for schedule read-outs.

### Inputs

All inputs are optional — a bare call bootstraps with the default lookback.

* `cursor_key`: Durable cursor name, e.g. the office code. When set, the poll position is loaded from the agent data store and advanced after each successful poll — see **Durable cursor** below. Recommended for scheduled polling; use one key per office.
* `changed_since`: The cursor from the previous call's `next_cursor`. Accepts AdvancedMD format (`MM/DD/YYYY H:MM:SS AM/PM`), RFC3339, or `YYYY-MM-DD HH:MM:SS`. Values with no offset are read as office-local time; an RFC3339 offset is converted into `timezone` first, so `2026-07-27T16:00:00Z` is sent as `07/27/2026 9:00:00 AM` for a Pacific office. An explicit value here overrides a `cursor_key` read.
* `lookback_minutes`: Bootstrap window used only when `changed_since` is absent. Defaults to `60`.
* `timezone`: IANA timezone of the AdvancedMD office. Used to build the bootstrap window, and to convert a `changed_since` that carries an offset. Defaults to `America/Los_Angeles` — **set it explicitly for a non-Pacific office**, or the bootstrap window is shifted by that offset (eastward offices over-read, which dedup absorbs; the default is not safe to rely on).
* `update_status`: Keep only these change kinds — `added`, `modified`, `deleted`. Defaults to all. **Filtering is lossy across polls:** the cursor advances past the kinds you exclude, so they can never be fetched again for those windows. The excluded count comes back as `filtered_out`.
* `limit`: Maximum visits to accept. Defaults to `200`. More changes than this is an **error**, not a clipped result — see Notes.

### The cursor contract

`next_cursor` is AdvancedMD's own server clock, read from the response's `servertime`. **Always round-trip it into the next poll** rather than computing a timestamp locally.

Note the two ends of the round trip use **different formats**. AdvancedMD returns
`servertime` as ISO with milliseconds (`2026-01-07T04:14:01.753`) but accepts
`@datechanged` only as `MM/DD/YYYY H:MM:SS AM/PM`. The tool parses both and
converts, so a returned `next_cursor` can be fed straight back in — but a caller
comparing the two strings will see them differ.

Two failure modes this avoids:

* **Clock skew drops visits.** AdvancedMD interprets `@datechanged` in office-local time. A worker running in UTC that computes "now minus 5 minutes" sends a timestamp 7–8 hours in the future for a US Pacific office, and the poll silently returns nothing. This is why the bootstrap path takes a `timezone` rather than using UTC.
* **Restarting from "now" skips the gap.** If a response carries no `servertime`, the tool returns the input cursor (or the bootstrap window start) as `next_cursor` instead, so the next poll re-reads the window rather than skipping everything that changed during it. The input cursor string is kept verbatim — re-formatting would drop its millisecond precision.

### Durable cursor (`cursor_key`)

Without `cursor_key`, the agent must carry `next_cursor` between polls itself — impossible
for a scheduled agent whose runs are separate tasks, which is why every run would otherwise
bootstrap from `lookback_minutes` and lean on downstream dedup. With `cursor_key`:

* The position is stored in the agent data store under the reserved record type
  `system/advancedmd-visit-cursor` (created automatically on first use), scoped to the agent
  group so different agents keep independent positions.
* When `changed_since` is omitted, the stored position drives the window; only the very
  first run bootstraps via `lookback_minutes`.
* After a successful poll the stored position advances under a monotonic guard: a retried
  or out-of-order poll can never move it backwards. When a newer position already stands
  (another run advanced past this window), the result reports `cursor_persisted: false`,
  returns the standing position as `next_cursor`, and warns that the window was likely
  already handled.
* If the cursor cannot be persisted, the call **fails** rather than returning visits — an
  unpersisted cursor means the next run re-reads the window, and the caller's CRM writes
  are not idempotent.
* An explicit `changed_since` that starts **after** the stored position polls normally but
  does **not** advance the stored cursor (`cursor_persisted: false` with an explanatory
  note) — advancing would permanently skip the visits between the stored position and the
  override. An explicit `changed_since` at or before the stored position (a re-read)
  advances as usual.
* **Known DST limitation.** The stored position compares as an office-local wall-clock
  string with no UTC offset. Around a fall-back transition the repeated hour can make two
  real-time-ordered `servertime` values compare out of order, so an advance inside that
  hour may be refused and the window re-polled next run. The guard errs toward re-reading
  (dedup absorbs it) — it never skips forward.

Output adds `cursor_key`, `cursor_source` (`store`, `argument` or `bootstrap`) and
`cursor_persisted` when `cursor_key` is used.

### Output

Structured content includes:

* `changed_since`: the window start actually sent to AdvancedMD
* `next_cursor`: cursor for the next poll
* `bootstrapped`: `true` when the lookback path was used instead of a supplied cursor
* `total_changed`: how many visits AdvancedMD reported changed, before `update_status` filtering — this is what `limit` is checked against
* `count`: number of visits returned
* `filtered_out`: present only when `update_status` excluded some, since those fall behind the cursor
* `visits`: array of visit records

Each visit carries `update_status` — `added`, `modified` or `deleted`, normalised from the `A`/`M`/`D` codes AdvancedMD puts on the wire, so it matches the vocabulary the `update_status` filter takes — plus scheduling fields, `facility_code`, `created_by`, and a nested `patient_list.patient` with chart number, name, phones, email and insurance list.

`created_by` identifies the booking user, which is how a self-scheduling source is attributed — e.g. a `zoc…` username for Zocdoc bookings versus `nex…` for NextPatient. The attribute itself resolves: it came back populated on every visit of a live `getupdatedvisits` payload (2026-08-03). The `zoc…`/`nex…` prefix convention, however, is taken from the AdvancedMD UI and has **not** been confirmed against a real self-scheduled booking — treat a prefix match as a heuristic until it has.

Example shape:

```json theme={null}
{
  "changed_since": "07/27/2026 8:00:00 AM",
  "next_cursor": "07/27/2026 9:15:00 AM",
  "bootstrapped": false,
  "count": 1,
  "visits": [
    {
      "id": "vst1",
      "update_status": "added",
      "visit_date": "07/28/2026",
      "visit_start_time": "09:00",
      "appointment_type": "NP EVAL",
      "provider_name": "Dr Smith",
      "facility_code": "151547",
      "created_by": "zoc09152025",
      "patient_list": {
        "patient": {
          "id": "patPT1",
          "chart": "CH1",
          "name": "Doe, Jane",
          "email": "jane@example.com"
        }
      }
    }
  ]
}
```

### Notes

* Field selection is fixed by the tool: AdvancedMD only returns attributes named in the request, and the tool asks for the scheduling, patient-contact and insurance fields an intake flow needs.
* **Over-`limit` is an error, not a clipped result.** `getupdatedvisits` takes no offset, so a clipped window cannot be paged: returning part of it would either lose the remainder (if the caller advances the cursor) or re-read the same window forever (if it holds). Instead the tool fails with the actual count and leaves the cursor unadvanced, so an unattended poller surfaces the problem rather than stalling on a billed no-op loop. Raise `limit` above the reported count, or poll a narrower window.
* Deduplication is the caller's responsibility. A visit modified twice between polls appears once, but a visit that stays modified across successive windows can appear in more than one poll.
* **Silent capping is rejected.** `Results/@visitcount` is compared against the number of visit elements returned; a shortfall means AdvancedMD capped the set without raising a fault, so the tool errors rather than advancing the cursor past visits it never saw. Verified on a live office that the two agree at 0, 4 and 95 visits.
* **`servertime` clock.** Measured on a live office as office-local (`01:36:11` against `America/Los_Angeles` `01:36:09`), not a UTC or Mountain server clock. Round-tripping it is therefore consistent for a Pacific office. A single office cannot prove the value is *derived* from the office rather than a fixed AdvancedMD clock that happens to be Pacific — worth re-checking against a non-Pacific office before relying on the cursor there.
* **Only-IDs mode.** When a window matches too many visits, AdvancedMD returns a fault (wording varies — "only IDs", "too many", "truncated") and omits the visit attributes. The tool surfaces this as a tool error rather than an empty success, so the caller must **not** advance its cursor; retry with a narrower window. Bootstrapping with a very large `lookback_minutes` is the usual way to trip this.
* **Timezone data is embedded.** The package imports `time/tzdata`, so named zones resolve without OS tzdata. The worker runtime image is `alpine` + `ca-certificates` only, where `time.LoadLocation` would otherwise fail with "unknown timezone" on the default `America/Los_Angeles`.
* **Multi-office tenants.** One call covers one office code, since credentials are per office. A tenant with several office keys needs one call per office and a cursor per office; taking the max `servertime` across offices will skip visits in whichever office lags.
* Do not advance a stored cursor when the call errors or returns no `servertime` — the window was never observed, and advancing past it drops those visits permanently.
