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

# Read complete appointment and bill histories for up to 25 charts

> Every page in a response is read from one database snapshot. Each (chart, resource) pages on its own cursor, so a large chart cannot hold back the others. A cursor only continues the exact set it was cut from: if any row of that chart's resource changed since, the resource comes back `snapshot_changed` and is restarted without a cursor. `readiness` says whether the platform has evidence the mirror holds the EHR's rows — read it before treating an empty history as 'no visits'. Unknown fields are rejected.



## OpenAPI

````yaml /openapi-v4.json post /patients/history
openapi: 3.0.0
info:
  title: Max AI Public API
  description: API for third-party marketplace apps
  version: '4.0'
  contact: {}
servers:
  - url: https://api.maxcare.ai/v4
security: []
tags: []
paths:
  /patients/history:
    post:
      tags:
        - Patients
      summary: Read complete appointment and bill histories for up to 25 charts
      description: >-
        Every page in a response is read from one database snapshot. Each
        (chart, resource) pages on its own cursor, so a large chart cannot hold
        back the others. A cursor only continues the exact set it was cut from:
        if any row of that chart's resource changed since, the resource comes
        back `snapshot_changed` and is restarted without a cursor. `readiness`
        says whether the platform has evidence the mirror holds the EHR's rows —
        read it before treating an empty history as 'no visits'. Unknown fields
        are rejected.
      operationId: PatientHistoryPublicController_readHistory
      parameters:
        - name: X-Organization-Id
          in: header
          required: true
          schema:
            type: string
          description: Target clinic organization ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatientHistoryBody'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PatientHistorySuccessResponse'
        '400':
          description: Missing or invalid request parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiBadRequestResponse'
        '401':
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiUnauthorizedResponse'
        '403':
          description: Insufficient scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiForbiddenResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiRateLimitResponse'
      security:
        - api-key: []
components:
  schemas:
    PatientHistoryBody:
      type: object
      properties:
        patientIds:
          description: >-
            Charts to read, 1–25. Each is answered independently: a chart
            outside your organization comes back `found: false`, never as an
            error that fails the batch.
          example:
            - pat_c56103bcd39c46d39f3138dd2b5e05f6
          type: array
          items:
            type: string
        resources:
          type: array
          description: >-
            Resources to read. Defaults to both. `appointments` needs
            `read:appointments`, `bills` needs `read:bills`.
          example:
            - appointments
            - bills
          items:
            type: string
            enum:
              - appointments
              - bills
        limit:
          type: number
          description: >-
            Rows per patient per resource, 1–200 (default 100). Lowered to fit
            2500 rows per response; the applied value is echoed as
            `limitPerResource`.
          example: 100
        coverage:
          description: >-
            Window appointment `readiness` is judged over. Defaults to the last
            two years through today (UTC). Up to ten years.
          allOf:
            - $ref: '#/components/schemas/HistoryCoverageWindow'
        cursors:
          description: >-
            `nextCursor` values from a previous response. Each names its own
            patient and resource; a (patient, resource) without a cursor starts
            from its first row. Every cursor must name a patient in `patientIds`
            and a requested resource.
          type: array
          items:
            type: string
      required:
        - patientIds
    PatientHistorySuccessResponse:
      type: object
      properties:
        code:
          type: string
          description: Response code
          example: success
        data:
          $ref: '#/components/schemas/PatientHistoryData'
      required:
        - code
        - data
    PublicApiBadRequestResponse:
      type: object
      properties:
        code:
          type: string
          description: Error code
          example: bad_request
        message:
          type: string
          description: Human-readable error message
          example: '''id'' must be a valid UUID'
        trace_id:
          type: string
          description: Trace ID for debugging
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - code
        - message
        - trace_id
    PublicApiUnauthorizedResponse:
      type: object
      properties:
        code:
          type: string
          description: Error code
          example: unauthorized
        message:
          type: string
          description: Human-readable error message
          example: Invalid or missing API key
        trace_id:
          type: string
          description: Trace ID for debugging
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - code
        - message
        - trace_id
    PublicApiForbiddenResponse:
      type: object
      properties:
        code:
          type: string
          description: Error code
          example: forbidden
        message:
          type: string
          description: Human-readable error message
          example: Insufficient scope
        trace_id:
          type: string
          description: Trace ID for debugging
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - code
        - message
        - trace_id
    PublicApiRateLimitResponse:
      type: object
      properties:
        code:
          type: string
          description: Error code
          example: rate_limit_exceeded
        message:
          type: string
          description: Human-readable error message
          example: Rate limit exceeded. Maximum 1000 requests per 60 seconds.
        trace_id:
          type: string
          description: Trace ID for debugging
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - code
        - message
        - trace_id
    HistoryCoverageWindow:
      type: object
      properties:
        from:
          type: string
          description: >-
            First facility-local day to judge appointment coverage for,
            `YYYY-MM-DD`.
          example: '2024-10-01'
        to:
          type: string
          description: Last day, `YYYY-MM-DD`, inclusive.
          example: '2026-10-06'
      required:
        - from
        - to
    PatientHistoryData:
      type: object
      properties:
        snapshotAt:
          type: string
          description: >-
            Database time of the snapshot every page in this response was read
            from.
        limitPerResource:
          type: number
          description: Rows per patient per resource applied to this response.
        patients:
          type: array
          items:
            $ref: '#/components/schemas/PatientHistoryEntry'
        groups:
          description: >-
            One entry per linked identity among the requested charts, so a
            group's completeness reads in one place.
          type: array
          items:
            $ref: '#/components/schemas/HistoryGroup'
      required:
        - snapshotAt
        - limitPerResource
        - patients
        - groups
    PatientHistoryEntry:
      type: object
      properties:
        patientId:
          type: string
          description: The id as requested.
        found:
          type: boolean
          description: >-
            False for an id that is not a chart in your organization, whether or
            not it exists elsewhere.
        chart:
          $ref: '#/components/schemas/HistoryChart'
        appointments:
          $ref: '#/components/schemas/HistoryAppointmentPage'
        bills:
          $ref: '#/components/schemas/HistoryBillPage'
      required:
        - patientId
        - found
    HistoryGroup:
      type: object
      properties:
        masterPatientId:
          type: string
          example: mpt_4d3c2b1a0f9e8d7c6b5a4f3e2d1c0b9a
        memberIds:
          description: Every chart in your organization linked to this identity.
          type: array
          items:
            type: string
        missingMemberIds:
          description: >-
            Members that were not requested. A group is not complete until all
            are read.
          type: array
          items:
            type: string
        appointments:
          $ref: '#/components/schemas/HistoryGroupResource'
        bills:
          $ref: '#/components/schemas/HistoryGroupResource'
      required:
        - masterPatientId
        - memberIds
        - missingMemberIds
    HistoryChart:
      type: object
      properties:
        id:
          type: string
          example: pat_c56103bcd39c46d39f3138dd2b5e05f6
        instanceId:
          type: string
          description: EHR instance the chart belongs to.
          example: ins_0b6f2a1c9d8e4f7a8b6c5d4e3f2a1b0c
        ehrType:
          type: string
          description: EHR of the instance, lowercase (`modmed`, `ezderm`).
          example: modmed
        ehrPatientId:
          type: string
          description: The EHR's own patient id for this chart.
          nullable: true
          example: '112233'
        mrn:
          type: string
          description: This chart's MRN in its own EHR. Linked charts keep their own MRNs.
          nullable: true
        updatedAt:
          type: string
          description: >-
            The chart's change version: advances on demographic, contact,
            address and MRN changes on the chart row and when its identity group
            gains or loses a member.
          example: '2026-10-01T14:03:11.412Z'
        masterPatientId:
          type: string
          nullable: true
          example: mpt_4d3c2b1a0f9e8d7c6b5a4f3e2d1c0b9a
        duplicatePatientIds:
          description: >-
            Other charts in this organization linked to the same identity. Never
            populated from shared phone or email alone.
          type: array
          items:
            type: string
        identityLinks:
          description: >-
            Evidence behind each linked chart. `legacy_unverified` links are not
            proof of the same person.
          type: array
          items:
            $ref: '#/components/schemas/PatientIdentityLink'
        identityReview:
          description: >-
            Whether this chart is linked, waiting for identity review, or
            unmatched.
          allOf:
            - $ref: '#/components/schemas/PatientIdentityReview'
        identityMembershipVersion:
          type: string
          nullable: true
          description: >-
            Opaque version of the set of charts in this organization sharing
            this chart's identity; changes when one joins or leaves. Null when
            unlinked. Compare with `contactSources[*].membershipVersion` from
            `GET /v4/patients`.
      required:
        - id
        - instanceId
        - ehrType
        - ehrPatientId
        - mrn
        - updatedAt
        - masterPatientId
        - duplicatePatientIds
        - identityLinks
        - identityReview
        - identityMembershipVersion
    HistoryAppointmentPage:
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
            - snapshot_changed
          description: >-
            `snapshot_changed`: a cursor was sent and this patient's rows for
            the resource changed since it was issued. No items are returned;
            restart the resource without a cursor.
        version:
          type: string
          description: >-
            Content hash of every row in this (patient, resource) set at the
            snapshot. Equal versions mean identical sets; a cursor only
            continues while it holds.
          nullable: true
        totalCount:
          type: number
          description: Rows in the whole set at the snapshot, not on this page.
          nullable: true
        hasMore:
          type: boolean
        nextCursor:
          type: string
          nullable: true
        readiness:
          $ref: '#/components/schemas/HistoryReadiness'
        items:
          type: array
          items:
            $ref: '#/components/schemas/HistoryAppointment'
      required:
        - status
        - version
        - totalCount
        - hasMore
        - nextCursor
        - readiness
        - items
    HistoryBillPage:
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
            - snapshot_changed
          description: >-
            `snapshot_changed`: a cursor was sent and this patient's rows for
            the resource changed since it was issued. No items are returned;
            restart the resource without a cursor.
        version:
          type: string
          description: >-
            Content hash of every row in this (patient, resource) set at the
            snapshot. Equal versions mean identical sets; a cursor only
            continues while it holds.
          nullable: true
        totalCount:
          type: number
          description: Rows in the whole set at the snapshot, not on this page.
          nullable: true
        hasMore:
          type: boolean
        nextCursor:
          type: string
          nullable: true
        readiness:
          $ref: '#/components/schemas/HistoryReadiness'
        items:
          type: array
          items:
            $ref: '#/components/schemas/HistoryBill'
      required:
        - status
        - version
        - totalCount
        - hasMore
        - nextCursor
        - readiness
        - items
    HistoryGroupResource:
      type: object
      properties:
        status:
          type: string
          enum:
            - complete
            - stale
            - partial
            - failed
            - not_imported
            - in_progress
            - unverified
            - missing_members
          description: >-
            The least complete member's status, or `missing_members` when the
            group has charts in your organization that were not in `patientIds`.
      required:
        - status
    PatientIdentityLink:
      type: object
      properties:
        patientId:
          type: string
          example: pat_c56103bcd39c46d39f3138dd2b5e05f6
        basis:
          type: string
          enum:
            - name_dob
            - manual
            - legacy_name_dob_match
            - legacy_unverified
          description: >-
            `name_dob`: linked automatically on first name, last name and date
            of birth (never on phone or email alone). `manual`: linked by a
            person. `legacy_name_dob_match`: linked before the basis was
            recorded and the charts' names and dates of birth agree.
            `legacy_unverified`: linked before the basis was recorded with
            nothing on the charts corroborating it — it may rest on a shared
            household phone or email; do not treat it as the same person.
      required:
        - patientId
        - basis
    PatientIdentityReview:
      type: object
      properties:
        status:
          type: string
          enum:
            - linked
            - pending_review
            - unmatched
          description: >-
            `linked`: the record has an identity (`masterPatientId`).
            `pending_review`: identity matching found identities it would not
            link automatically (some identifiers agreed, others did not) and is
            waiting for a person; the record stays unlinked until then.
            `unmatched`: no identity and no review waiting.
        candidates:
          description: >-
            With `pending_review`: the identities in this organization the
            record might belong to. A candidate is not a link — do not merge on
            it. Empty for any other status.
          type: array
          items:
            $ref: '#/components/schemas/PatientIdentityReviewCandidate'
      required:
        - status
        - candidates
    HistoryReadiness:
      type: object
      properties:
        status:
          type: string
          enum:
            - complete
            - stale
            - partial
            - failed
            - not_imported
            - in_progress
            - unverified
          description: >-
            Appointments, for the requested window: `complete` when every scope
            of the chart's EHR instance has a verified sweep for every day;
            `stale` when complete but the last week's days were last verified
            more than an hour ago; `partial` when some days are unverified;
            `failed` when an unverified day's latest sweep failed;
            `not_imported` when no day is verified. Bills: `in_progress` while
            any of this chart's bills is discovered but not yet read in full,
            otherwise `unverified` — bill discovery keeps no completeness
            evidence, so the platform does not call a bill history complete.
        requestedFrom:
          type: string
          description: 'Appointments: first day of the window judged.'
        requestedTo:
          type: string
          description: 'Appointments: last day of the window judged.'
        asOf:
          type: string
          nullable: true
          description: >-
            Appointments, when complete: oldest verification across scopes and
            days — the EHR state the window reflects.
        liveAsOf:
          type: string
          nullable: true
          description: 'Appointments: oldest verification of the last week''s days.'
        excludes:
          description: >-
            Appointment kinds the sync never imports for this EHR, e.g.
            `virtual_encounters` on EZDerm.
          type: array
          items:
            type: string
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/HistoryCoverageScope'
        pendingHydration:
          type: number
          description: 'Bills: this chart''s bills discovered but not yet read in full.'
        basis:
          type: string
          enum:
            - window
            - patient
          description: >-
            Appointments: what `status` rests on — `window` (every facility-day
            of the window was swept) or `patient` (a refresh read this chart's
            whole history in one patient-scoped call, see POST
            /v4/patients/history/refresh).
        patientRefresh:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/HistoryPatientRefresh'
      required:
        - status
    HistoryAppointment:
      type: object
      properties:
        id:
          type: string
          example: apt_9f8e7d6c5b4a39281706f5e4d3c2b1a0
        ehrAppointmentId:
          type: string
          description: The EHR's own appointment id.
        status:
          type: string
          example: scheduled
        integrationStatus:
          type: string
          nullable: true
          example: CHECKED_IN
        scheduledStartDate:
          type: string
        scheduledEndDate:
          type: string
          nullable: true
        facilityId:
          type: string
          nullable: true
        ehrFacilityId:
          type: string
          nullable: true
          description: >-
            The EHR's own facility id, kept even when the facility is not (or no
            longer) mirrored.
        providerId:
          type: string
          nullable: true
        appointmentTypeName:
          type: string
          nullable: true
        isNewPatient:
          type: boolean
          nullable: true
        isCosmetic:
          type: boolean
          nullable: true
        createdAt:
          type: string
        lastSyncedAt:
          type: string
          description: Last time a sync changed this row.
        removedFromEhrAt:
          type: string
          nullable: true
          description: >-
            Set when a sweep that read this appointment's whole week did not
            return it: deleted in the EHR, or moved to a week not yet re-read.
            Cleared if a later sweep returns it. Such an appointment should not
            count as a visit or a booking.
      required:
        - id
        - ehrAppointmentId
        - status
        - integrationStatus
        - scheduledStartDate
        - scheduledEndDate
        - facilityId
        - ehrFacilityId
        - providerId
        - appointmentTypeName
        - isNewPatient
        - isCosmetic
        - createdAt
        - lastSyncedAt
        - removedFromEhrAt
    HistoryBill:
      type: object
      properties:
        id:
          type: string
          example: bil_0a1b2c3d4e5f46a7b8c9d0e1f2a3b4c5
        ehrBillId:
          type: string
          description: The EHR's own bill / encounter id.
        dateOfService:
          type: string
        status:
          type: string
          description: Workflow status. `new` means discovered but not yet read in full.
        isBillable:
          type: boolean
          nullable: true
          description: False when the EHR flags the visit non-billable; still a visit.
        isCosmetic:
          type: boolean
        facilityId:
          type: string
          nullable: true
        providerId:
          type: string
          nullable: true
        updatedAt:
          type: string
        lastSyncedAt:
          type: string
          nullable: true
          description: Null until the bill has been read in full.
        codingGroups:
          description: Non-archived coding groups.
          type: array
          items:
            $ref: '#/components/schemas/HistoryCodingGroup'
      required:
        - id
        - ehrBillId
        - dateOfService
        - status
        - isBillable
        - isCosmetic
        - facilityId
        - providerId
        - updatedAt
        - lastSyncedAt
        - codingGroups
    PatientIdentityReviewCandidate:
      type: object
      properties:
        masterPatientId:
          type: string
          example: mpt_4d3c2b1a0f9e8d7c6b5a4f3e2d1c0b9a
        matchedOn:
          type: array
          description: >-
            Which identifiers agreed with this identity. A candidate matched on
            `phone` or `email` alone may be a relative sharing household contact
            details.
          items:
            type: string
            enum:
              - name_dob
              - phone
              - email
      required:
        - masterPatientId
        - matchedOn
    HistoryCoverageScope:
      type: object
      properties:
        scope:
          type: string
          description: >-
            `fac_…` for a per-facility sweep (ModMed), `practice` for a
            practice-wide one (EZDerm).
        status:
          type: string
          enum:
            - complete
            - partial
            - not_imported
            - failed
        verifiedDays:
          type: number
          description: Days in the requested window with a verified sweep.
        totalDays:
          type: number
          description: Days in the requested window.
        firstUnverifiedDay:
          type: string
          nullable: true
          description: Earliest day in the window with no verified sweep.
        lastUnverifiedDay:
          type: string
          nullable: true
          description: Latest day in the window with no verified sweep.
        asOf:
          type: string
          nullable: true
          description: >-
            Oldest verification among the window's days: the EHR state the whole
            window reflects.
        liveAsOf:
          type: string
          nullable: true
          description: >-
            Oldest verification among the window's days in the last week, which
            the rolling sync re-reads.
        lastFailureAt:
          type: string
          nullable: true
        lastFailureReason:
          type: string
          nullable: true
          example: page-unread
      required:
        - scope
        - status
        - verifiedDays
        - totalDays
        - firstUnverifiedDay
        - lastUnverifiedDay
        - asOf
        - liveAsOf
        - lastFailureAt
        - lastFailureReason
    HistoryPatientRefresh:
      type: object
      properties:
        status:
          type: string
          enum:
            - complete
            - stale
            - in_progress
            - failed
            - not_requested
        verifiedAt:
          type: string
          nullable: true
          description: Start of the last read that returned the whole history.
        requestedAt:
          type: string
          nullable: true
        lastFailureAt:
          type: string
          nullable: true
        lastFailureReason:
          type: string
          nullable: true
          example: possible-truncation
      required:
        - status
        - verifiedAt
        - requestedAt
        - lastFailureAt
        - lastFailureReason
    HistoryCodingGroup:
      type: object
      properties:
        id:
          type: string
          example: cgr_1a2b3c4d5e6f47a8b9c0d1e2f3a4b5c6
        dataSource:
          type: string
          description: >-
            `ehr` is the live EHR mirror; `ehr0` a frozen first-seen snapshot;
            `agent`/`user`/`manual` are not EHR coding.
          example: ehr
        status:
          type: string
          nullable: true
        updatedAt:
          type: string
        diagnoses:
          type: array
          items:
            $ref: '#/components/schemas/HistoryDiagnosis'
        lineItems:
          type: array
          items:
            $ref: '#/components/schemas/HistoryLineItem'
      required:
        - id
        - dataSource
        - status
        - updatedAt
        - diagnoses
        - lineItems
    HistoryDiagnosis:
      type: object
      properties:
        code:
          type: string
          example: L57.0
        position:
          type: number
      required:
        - code
        - position
    HistoryLineItem:
      type: object
      properties:
        code:
          type: string
          example: '17000'
        units:
          type: string
          example: '1.000'
        position:
          type: number
          nullable: true
      required:
        - code
        - units
        - position
  securitySchemes:
    api-key:
      scheme: bearer
      bearerFormat: JWT
      type: http
      description: Marketplace API key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.