Skip to main content
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

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_Angelesset 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_statusadded, 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:

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.