Skip to content

MCP reference / Patients

Patient tools

Fourteen tools covering the patient record, its clinical notes, its courses of treatment, and the sub-resources hanging off it. Each one with its arguments, an example call, and what it returns.

How to read this

Each tool below lists its arguments exactly as the input schema declares them: the type, whether it is required, the permitted values where the schema fixes them, and what the schema says the argument means. None of it is written here. An amount in cents, a default that applies when a field is left out, which action needs which id: those sentences come from the tool itself.

practiceId is never an argument. It comes from your token, and the write paths strip it if a client sends one anyway.

A tool that writes shows one example per action, because what an action needs is not what the input schema marks required: the schema requires action and leaves the rest to the action. Every example is a complete tools/call request whose arguments are checked, when this page is built, against the same object the handler parses. Errors come back inside a 200 response with isError set: the MCP overview has that envelope, and the full catalogue lists every tool at a glance.

Patients

The patient record, its notes, its courses of treatment, and the sub-resources hanging off it.

list_patients

Read-onlyAny member

List patients in the practice, with optional search, status and tag filters.

Arguments

ToolTypeRequired?What it means
searchstringOptionalMatches against name, email and phone. This is the argument — the `search` tool calls the same thing `q`, and sending that name here used to return the first page of every patient instead of a search. Both work now, and the answer echoes what it searched for.
qstringOptionalAn alias for `search`. Prefer `search`.
statusstringOptionalOne of this practice's pack-defined status ids — read them from get_me (pack.patientStatuses) rather than assuming.
tagIdstringOptionalUse list_tags to find tag ids.
includeInactivebooleanOptionalAlso list inactive records (archived, no longer seen), marked isActive=false. The answer always says how many it left out (inactiveCount).
limitintegerOptionalHow many rows to return. Defaults to 50; at most 200 (a larger value is lowered to it, not rejected).
offsetintegerOptionalHow many rows to skip, for paging through a long list. At most 100000.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patients",
    "arguments": {}
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

get_patient

Read-onlyAny member

Read a single patient by id.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_patient",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_patient_notes

Read-onlyAny member

List every clinical note recorded against a patient.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_notes",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_patient_files

Read-onlyAny member

List files attached to a patient. Use get_file for a download URL.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_files",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_patient_billing_entries

Read-onlyAny member

List the billing entries recorded against a patient, with balance: what the patient owes now, the same figure as the ledger on the patient's page. Quote it rather than adding entries up. A receipt entry is a payment, not a document.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired
includeVoidedbooleanOptionalAlso list voided (cancelled) rows, marked voided=true. Only for a question about what was voided: they count for nothing. The answer always says how many it left out (voidedCount).
limitintegerOptionalHow many entries to return, the newest kept. The answer says when older ones were left out (truncated); totals always cover every entry matched.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_billing_entries",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_patient_invoices

Read-onlyAny member

List the invoices already issued for a patient.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired
includeVoidedbooleanOptionalAlso list voided (cancelled) rows, marked voided=true. Only for a question about what was voided: they count for nothing. The answer always says how many it left out (voidedCount).

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_patient_invoices",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

get_patient_invoice_preview

Read-onlyAny member

Compute the draft invoice for a patient's unbilled entries without issuing it. Use write_invoice with action=issue to issue one.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_patient_invoice_preview",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

write_patient

WritesCan deleteAny member

Create, update or delete a patient. action=create requires firstName and lastName; update and delete require patientId. Deleting is permanent. A create for a name the practice already has is refused with patient_exists; differentPerson: true adds the record anyway, once the practice says it is somebody else. The server cannot check that the practice said so, and the activity log records it.

Arguments

ToolTypeRequired?What it means
actionstring: create | update | deleteRequired
patientIdstringOptionalThe patient to update or delete.
firstNamestringOptionalRequired for action=create.
lastNamestringOptionalRequired for action=create.
dobstringOptionalDate of birth, YYYY-MM-DD. Send it ONLY if the caller stated it — never a placeholder, never the earliest date this field accepts, never a guess from anything else on the record. A patient with no date of birth stays that way until somebody tells you theirs. Must be a real past date, and the practice's own records are the only source for it.
genderstringOptionalFree text — the practice records whatever the patient told them.
emailstringOptionalThe patient's email address, only if the caller gave it. A patient with no email stays that way: do not compose one from their name, and do not send an example address.
phoneMobilestringOptionalMobile number, as the practice records it, and only if the caller gave it. Never a placeholder such as a run of zeros: a patient with no number stays that way.
addressstringOptionalStreet address, free text.
postcodestringOptionalPostal code.
countrystringOptionalCountry, free text.
socialSecurityNumberstringOptionalThe patient's social security number (in Greece, the ΑΜΚΑ). Send an empty string to clear it.
taxIdstringOptionalThe patient's tax id (in Greece, the ΑΦΜ). Only needed for a tax invoice (write_invoice kind=invoice). Send an empty string to clear it.
taxOfficestringOptionalThe tax office the patient's tax id is registered with (in Greece, the ΔΟΥ).
statusstringOptionalMust be one of the status ids defined by this practice's vertical pack — the values differ per pack, so read them from get_me (pack.patientStatuses) rather than assuming. Any other value is rejected.
notifyByEmailbooleanOptionalWhether the patient consents to email reminders. Defaults to false when a patient is created without it. Booking emails (booked, confirmed, cancelled) are sent regardless.
notifyBySmsbooleanOptionalWhether the patient consents to SMS reminders. Defaults to false when a patient is created without it.
fileUseConsentboolean | nullOptionalWhether the patient consents to their files (x-rays, scans, photos) being used for teaching or promotion. Null means nobody has asked, which is NOT a refusal, and is what a patient created without it holds. Send it only when the practice said what the patient answered: never infer it.
differentPersonbooleanOptionalaction=create only, and only after a patient_exists refusal and the practice saying this is somebody else with the same name. The server cannot check that the practice said so; the new record's activity log entry names the existing ones.

Example call

action=create

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient",
    "arguments": {
      "action": "create",
      "firstName": "Anna",
      "lastName": "Papadopoulou",
      "phoneMobile": "+30 694 000 0000"
    }
  }
}

action=update

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient",
    "arguments": {
      "action": "update",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "email": "anna.papadopoulou@example.com",
      "notifyBySms": true
    }
  }
}

action=delete

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient",
    "arguments": {
      "action": "delete",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
patient_exists500action=create for a name a patient of the practice already has. The refusal names that patient.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

write_patient_note

WritesCan deleteAny member

Create, update or delete a note on a patient. action=create requires body; update and delete require noteId.

Arguments

ToolTypeRequired?What it means
actionstring: create | update | deleteRequired
patientIdstringRequiredThe patient the note belongs to.
noteIdstringOptionalThe note to update or delete.
titlestringOptionalOptional heading for the note.
bodystringOptionalThe note's text. Required for action=create, and must be non-empty.

Example call

action=create

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient_note",
    "arguments": {
      "action": "create",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "title": "Check-up",
      "body": "Reviewed the upper right quadrant. No new findings."
    }
  }
}

action=update

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient_note",
    "arguments": {
      "action": "update",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "noteId": "1a2b3c4d-7e8f-4a5b-8c9d-0e1f2a3b4c5d",
      "body": "Reviewed the upper right quadrant. Recall in six months."
    }
  }
}

action=delete

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_patient_note",
    "arguments": {
      "action": "delete",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "noteId": "1a2b3c4d-7e8f-4a5b-8c9d-0e1f2a3b4c5d"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

toggle_patient_tag

WritesAny member

Add the tag to the patient if absent, remove it if already present.

Arguments

ToolTypeRequired?What it means
patientIdstringRequired
tagIdstringRequiredUse list_tags to find tag ids.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "toggle_patient_tag",
    "arguments": {
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "tagId": "70819203-3e4f-4091-8c5d-6e7f8091a2b3"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

list_treatment_courses

Read-onlyAny member

List a patient's courses of treatment, open ones first: status, dates and duration, diagnosis and plan, the diagnosis and treatment types they are tagged with, the next step, and the visit log.

Arguments

ToolTypeRequired?What it means
patientIdstringRequiredThe patient whose record this is.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_treatment_courses",
    "arguments": {
      "patientId": "b065ac83-b065-4c3a-9e21-7b84c5d10f33"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

list_treatment_picklists

Read-onlyAny member

List the practice's diagnosis types and treatment types, the words a course is tagged with. Archived ones on request.

Arguments

ToolTypeRequired?What it means
kindstring: diagnosis | treatmentOptionalOnly this list: diagnosis or treatment. Omit for both.
includeArchivedbooleanOptionalAlso list archived types, marked archived=true.

Example call

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_treatment_picklists",
    "arguments": {}
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the requested records" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.

write_treatment_course

WritesCan deleteAny member

Start, change or remove a course of treatment. action=create requires title; update and void require courseId and expectedUpdatedAt. A removed course keeps its row and its visits, out of the list.

Arguments

ToolTypeRequired?What it means
actionstring: create | update | voidRequired
patientIdstringRequiredThe patient whose record this is.
courseIdstringOptionalThe course to change or remove, from list_treatment_courses.
titlestringOptionalWhat the course is, in a few words («Orthodontic treatment», «Physiotherapy, left knee»). Required for action=create.
statusstring: planned | in_progress | completed | discontinuedOptionalWhere the course stands. action=create defaults to planned. completed and discontinued end it.
diagnosisstring | nullOptionalWhat was found, as the user said it. Send null to clear it.
planstring | nullOptionalThe treatment plan, as the user said it. Send null to clear it.
diagnosisTypeIdsarray<string>OptionalIds of the practice's diagnosis types (list_treatment_picklists kind=diagnosis), in the order shown. Replaces the whole list; [] clears it. An id that is not this practice's, is of the other list, or is archived (unless the course already has it) is refused (400, unknown_picklist_item). Only types the user named.
treatmentTypeIdsarray<string>OptionalIds of the practice's treatment types (list_treatment_picklists kind=treatment), in the order shown. Replaces the whole list; [] clears it. An id that is not this practice's, is of the other list, or is archived (unless the course already has it) is refused (400, unknown_picklist_item). Only types the user named.
startDatestring | nullOptionalWhen treatment started. YYYY-MM-DD; send null to clear it.
plannedEndDatestring | nullOptionalWhen treatment is planned to end; not before startDate. YYYY-MM-DD; send null to clear it.
nextStepDatestring | nullOptionalWhen the next step is due. YYYY-MM-DD; send null to clear it.
nextStepNotestring | nullOptionalWhat to do next, as the user said it. Send null to clear it.
expectedUpdatedAtstringOptionalThe course's updatedAt exactly as list_treatment_courses gave it. If it has changed since, nothing is written and the call fails with 409 (details[0].code: stale_version, or course_voided when it was removed): read again and retry. Never make one up.
reasonstring: entered_in_error | duplicate | otherOptionalWhy it is removed. Optional; a closed list, no free text is stored.

Example call

action=create

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_course",
    "arguments": {
      "action": "create",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "title": "Orthodontic treatment",
      "startDate": "2026-11-02"
    }
  }
}

action=update

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_course",
    "arguments": {
      "action": "update",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "courseId": "7a8b9cad-d8e9-4a2b-e6f7-08192a3b4c5d",
      "expectedUpdatedAt": "2026-11-02T08:30:00.000Z",
      "nextStepDate": "2026-12-01",
      "nextStepNote": "Adjust the archwire"
    }
  }
}

action=void

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_course",
    "arguments": {
      "action": "void",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "courseId": "7a8b9cad-d8e9-4a2b-e6f7-08192a3b4c5d",
      "expectedUpdatedAt": "2026-11-02T08:30:00.000Z",
      "reason": "entered_in_error"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
conflict409The record changed since you read it (details[0].code: stale_version), or it has since been voided. Read it again and retry. Never invent an expectedUpdatedAt.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

write_treatment_visit

WritesCan deleteAny member

Log, change or remove a visit on a course. action=create requires body, and may link the patient's appointment it was logged from; update and void require visitId and expectedUpdatedAt.

Arguments

ToolTypeRequired?What it means
actionstring: create | update | voidRequired
patientIdstringRequiredThe patient whose record this is.
courseIdstringRequiredThe course, from list_treatment_courses.
visitIdstringOptionalThe visit to change or remove, from list_treatment_courses.
visitDatestringOptionalThe day of the visit, YYYY-MM-DD. action=create defaults to the practice's today.
bodystringOptionalWhat was done at the visit, as the user said it. Required for action=create.
appointmentIdstringOptionalaction=create only: the patient's appointment this visit is logged from, from a read of their bookings. It must be one of this patient's (400 appointment_not_patients).
expectedUpdatedAtstringOptionalThe visit's updatedAt exactly as list_treatment_courses gave it. If it has changed since, nothing is written (409, stale_version or visit_voided): read again and retry.
reasonstring: entered_in_error | duplicate | otherOptionalWhy it is removed. Optional; a closed list, no free text is stored.

Example call

action=create

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_visit",
    "arguments": {
      "action": "create",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "courseId": "7a8b9cad-d8e9-4a2b-e6f7-08192a3b4c5d",
      "visitDate": "2026-11-02",
      "body": "Fitted the brackets on the upper arch."
    }
  }
}

action=update

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_visit",
    "arguments": {
      "action": "update",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "courseId": "7a8b9cad-d8e9-4a2b-e6f7-08192a3b4c5d",
      "visitId": "8b9cadbe-e9fa-4b3c-f708-192a3b4c5d6e",
      "expectedUpdatedAt": "2026-11-02T08:30:00.000Z",
      "body": "Fitted the brackets on the upper arch. Patient tolerated it well."
    }
  }
}

action=void

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_treatment_visit",
    "arguments": {
      "action": "void",
      "patientId": "0f1c7a2e-5b6d-4c3a-9e21-7b84c5d10f33",
      "courseId": "7a8b9cad-d8e9-4a2b-e6f7-08192a3b4c5d",
      "visitId": "8b9cadbe-e9fa-4b3c-f708-192a3b4c5d6e",
      "expectedUpdatedAt": "2026-11-02T08:30:00.000Z",
      "reason": "duplicate"
    }
  }
}

Result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": { "...": "the written record" },
    "content": [{ "type": "text", "text": "<the same JSON>" }]
  }
}

Errors

CodeStatusWhen
validation_failed400An argument is missing, of the wrong type, or outside its allowed values.
not_found404The id does not exist in your practice. A malformed id looks identical.
conflict409The record changed since you read it (details[0].code: stale_version), or it has since been voided. Read it again and retry. Never invent an expectedUpdatedAt.
plan_feature_required403The practice's plan does not include this. MCP needs Complete.
subscription_read_only403The subscription has lapsed, so the practice is read-only and nothing may be written.

Common questions

Patient records are encrypted, both where they are stored and on their way to your screen. Files are private, and a link to one stops working after a short time. Changes to patient records are recorded, with who made them and when. There is no shared practice password: everyone has their own account. On the Complete plan, the practice owner can also require two-step sign-in for the whole team.

Yes. The practice owner can export the patient list, the reports and the activity log to a spreadsheet file (CSV) from the app, and anyone in the practice can download its files at any time. Your practice data belongs to you, and there is no export fee or waiting period. That holds on either plan, during the trial, and after you cancel.

HyperCRM is paid software. Every practice starts with a 30-day free trial of the Complete plan, with no card needed. After that, Essential starts at €19 a month and Complete at €34, VAT included. The price depends on how many practitioners you have. Every price is on the pricing page, so you never need a sales call.