Connect your systems to Megvite.
Book visits, read charts and receive events from your EHR, hospital platform or partner app, with access your practice approves.
Three ways in
Every request runs as the clinic member who approved it, inside their clinic, with only the access they chose.
API keys
For server-to-server systems. A clinic owner or admin creates a key in Settings, under Megvite and partner access, and picks its scopes. The key is shown once.
OAuth 2.1
For apps used by many clinics. Authorization code with PKCE (S256). Register a client at /api/oauth/register, send members to /oauth/authorize, then exchange the code at /api/oauth/token.
MCP server
For connected assistants such as Claude and ChatGPT. Add https://megvite.com/api/mcp as a remote MCP server and sign in when asked.
curl https://megvite.com/api/v1/appointments?limit=10 \ -H "Authorization: Bearer $MEGVITE_API_KEY"
Scopes
A credential carries the scopes the member approved, narrowed on every call to what their current role allows.
- read:schedule
- See the schedule, availability and visit queue
- write:schedule
- Book and move appointments
- read:patients
- Search patients and read their contact details
- read:clinical
- Read chart summaries, care plans, lab and prescription orders
- write:drafts
- Draft visit notes, care plans and orders for a clinician to sign
- read:messages
- Read patient message threads
- write:messages
- Send replies in patient message threads
- read:analytics
- See clinic analytics
Endpoints
REST API v1. Open any endpoint for its parameters and a short example. The full contract is in the OpenAPI 3.1 document.
curl -X POST https://megvite.com/api/v1/patients \
-H "Authorization: Bearer $MEGVITE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d8e-intake-1042" \
-d '{"firstName":"Casey","lastName":"Rivera","dob":"1988-04-12"}'Patients
GET/api/v1/patientsList or search patientsread:patients
Newest first. Pass q to search by name, email, phone or MRN.
q: Search text. At least two characters.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "pat_8f2c41d0a7b94e1c55aa",
"firstName": "Casey",
"lastName": "Rivera",
"dob": "1988-04-12",
"sex": "F",
"email": "casey@example.test",
"phone": "+15555550100",
"mrn": "MRN-1042",
"preferredLanguage": "en",
"status": "active",
"createdAt": "2026-10-01T14:02:11.000Z"
}
],
"hasMore": false,
...GET/api/v1/patients/{id}Get a patientread:patients
id (required): Patient id.{
"data": {
"id": "pat_8f2c41d0a7b94e1c55aa",
"firstName": "Casey",
"lastName": "Rivera",
"dob": "1988-04-12",
"sex": "F",
"email": "casey@example.test",
"phone": "+15555550100",
"mrn": "MRN-1042",
"preferredLanguage": "en",
"status": "active",
"createdAt": "2026-10-01T14:02:11.000Z"
}
}POST/api/v1/patientsRegister a patientwrite:schedule
Registers a patient so they can be booked. Requires write:schedule. Send an Idempotency-Key header.
firstName (required): Given name.lastName (required): Family name.dob: Date of birth, YYYY-MM-DD.sex: F, M, X or U.email: Email address.phone: Phone number in E.164.mrn: Your medical record number. Unique per clinic.preferredLanguage: BCP 47 language tag. Default en.{
"firstName": "Casey",
"lastName": "Rivera",
"dob": "1988-04-12",
"email": "casey@example.test",
"mrn": "MRN-1042"
}{
"data": {
"id": "pat_8f2c41d0a7b94e1c55aa",
"firstName": "Casey",
"lastName": "Rivera",
"dob": "1988-04-12",
"sex": "F",
"email": "casey@example.test",
"phone": "+15555550100",
"mrn": "MRN-1042",
"preferredLanguage": "en",
"status": "active",
"createdAt": "2026-10-01T14:02:11.000Z"
}
}Appointments
GET/api/v1/appointmentsList appointmentsread:schedule
Ordered by scheduled time, earliest first.
from: Scheduled at or after this time.to: Scheduled before this time.status: Filter by status.patientId: Only this patient.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"providerId": "mem_prov",
"status": "scheduled",
"modality": "video",
"reason": "Follow-up review",
"scheduledFor": "2026-10-14T15:00:00.000Z",
"startedAt": null,
"endedAt": null,
"createdAt": "2026-10-11T09:30:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}GET/api/v1/appointments/{id}Get an appointmentread:schedule
id (required): Appointment id.{
"data": {
"id": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"providerId": "mem_prov",
"status": "scheduled",
"modality": "video",
"reason": "Follow-up review",
"scheduledFor": "2026-10-14T15:00:00.000Z",
"startedAt": null,
"endedAt": null,
"createdAt": "2026-10-11T09:30:00.000Z"
}
}POST/api/v1/appointmentsBook an appointmentwrite:schedule
Books a video or audio visit for a registered patient. Pass slotId to take an open slot from /availability, or scheduledFor for a time you choose.
patientId (required): Registered patient id.slotId: Open slot from GET /api/v1/availability.scheduledFor: Start time when no slotId is given.providerId: Clinician id. Taken from the slot when slotId is given.modality: Visit type.reason: Reason for visit, up to 500 characters.{
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"slotId": "slot_5d1e0a9c7b3f2e4d6a80",
"reason": "Follow-up review"
}{
"data": {
"id": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"providerId": "mem_prov",
"status": "scheduled",
"modality": "video",
"reason": "Follow-up review",
"scheduledFor": "2026-10-14T15:00:00.000Z",
"startedAt": null,
"endedAt": null,
"createdAt": "2026-10-11T09:30:00.000Z"
}
}POST/api/v1/appointments/{id}/rescheduleReschedule an appointmentwrite:schedule
id (required): Appointment id.slotId: New open slot.scheduledFor: New start time when no slotId is given.{
"scheduledFor": "2026-10-15T16:00:00.000Z"
}{
"data": {
"id": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"providerId": "mem_prov",
"status": "scheduled",
"modality": "video",
"reason": "Follow-up review",
"scheduledFor": "2026-10-15T16:00:00.000Z",
"startedAt": null,
"endedAt": null,
"createdAt": "2026-10-11T09:30:00.000Z"
}
}POST/api/v1/appointments/{id}/cancelCancel an appointmentwrite:schedule
id (required): Appointment id.reason: Why it was cancelled, up to 200 characters.{
"reason": "Patient asked to cancel"
}{
"data": {
"id": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"providerId": "mem_prov",
"status": "cancelled",
"modality": "video",
"reason": "Follow-up review",
"scheduledFor": "2026-10-14T15:00:00.000Z",
"startedAt": null,
"endedAt": null,
"createdAt": "2026-10-11T09:30:00.000Z"
}
}Availability
GET/api/v1/availabilityOpen appointment slotsread:schedule
from: Slots starting at or after this time. Default now.to: Slots starting before this time. Default 14 days out.providerId: Only this clinician.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "slot_5d1e0a9c7b3f2e4d6a80",
"providerId": "mem_prov",
"startsAt": "2026-10-14T15:00:00.000Z",
"endsAt": "2026-10-14T15:30:00.000Z",
"modality": "video",
"remaining": 1
}
],
"hasMore": false,
"nextCursor": null
}Messages
GET/api/v1/messages/threadsList message threadsread:messages
Most recently active first. Each thread carries its latest patient-visible message. Internal notes are never returned.
patientId: Only this patient.status: Thread status.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "thr_0b7c2e9d41a8f3e65c10",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"subject": "Question about my plan",
"status": "open",
"updatedAt": "2026-10-11T10:00:00.000Z",
"lastMessage": {
"id": "msg_3e1f",
"from": "patient",
"body": "Can I take it with food?",
"createdAt": "2026-10-11T10:00:00.000Z"
}
}
],
"hasMore": false,
...GET/api/v1/text-messagesList text messagesread:messages
Texts Megvite sent or received for the clinic, newest first: purpose, status and a masked number. Message text is never returned, and texts cannot be sent through the API.
appointmentId: Only texts about this appointment.limit: How many, 1 to 200. Default 50.{
"data": [
{
"id": "sms_4c1e9a7b20d35f8e61a0",
"direction": "outbound",
"purpose": "visit_link",
"status": "delivered",
"line": "megvite",
"phoneHint": "ending 0100",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"createdAt": "2026-10-14T14:50:00.000Z",
"updatedAt": "2026-10-14T14:50:04.000Z"
}
],
"hasMore": false,
"nextCursor": null
}POST/api/v1/messagesSend a message to a patientwrite:messages
Posts a provider message in a thread. Pass threadId to reply, or patientId to open a new thread.
threadId: Existing thread to reply in.patientId: Patient to start a new thread with.subject: Subject for a new thread.body (required): Message text, 1 to 4000 characters.{
"threadId": "thr_0b7c2e9d41a8f3e65c10",
"body": "Yes, take it with breakfast."
}{
"data": {
"id": "msg_91c0e7a2b4d5f6a8c3e1",
"threadId": "thr_0b7c2e9d41a8f3e65c10",
"from": "care_team",
"body": "Yes, take it with breakfast.",
"createdAt": "2026-10-11T10:05:00.000Z"
}
}Visit notes
GET/api/v1/visit-notesList signed visit notesread:clinical
Signed notes only, newest first. Drafts, psychotherapy notes and 42 CFR Part 2 records are never returned.
patientId: Only this patient.appointmentId: Only this appointment.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "sig_a17be0c4d2f9e81b3c55",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"version": 3,
"body": "S: ...\nO: ...\nA: ...\nP: ...",
"billingCode": "99213",
"signedBy": "mem_prov",
"signedAt": "2026-10-14T15:40:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}GET/api/v1/visit-notes/{id}Get a signed visit noteread:clinical
id (required): Signed note id.{
"data": {
"id": "sig_a17be0c4d2f9e81b3c55",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"version": 3,
"body": "S: ...\nO: ...\nA: ...\nP: ...",
"billingCode": "99213",
"signedBy": "mem_prov",
"signedAt": "2026-10-14T15:40:00.000Z"
}
}POST/api/v1/visit-notes/draftsCreate a draft visit notewrite:drafts
Adds a draft version to an appointment. A clinician reviews and signs it in Megvite. Signing is never available by API.
appointmentId (required): Appointment the note belongs to.body (required): Draft note text, 1 to 20000 characters.{
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"body": "S: Patient reports...\nA/P: ..."
}{
"data": {
"id": "cnv_6b0e1d2c3f4a5b6c7d8e",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"version": 4,
"status": "draft",
"createdAt": "2026-10-14T15:45:00.000Z"
}
}Care plans
GET/api/v1/care-plansList published care plansread:clinical
patientId: Only this patient.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "cpl_2a9d8c7b6e5f4a3b2c1d",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"version": 2,
"body": "Walk 20 minutes daily. Recheck in 6 weeks.",
"publishedAt": "2026-10-14T16:00:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}GET/api/v1/care-plans/{id}Get a published care planread:clinical
id (required): Care plan id.{
"data": {
"id": "cpl_2a9d8c7b6e5f4a3b2c1d",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"version": 2,
"body": "Walk 20 minutes daily. Recheck in 6 weeks.",
"publishedAt": "2026-10-14T16:00:00.000Z"
}
}Orders
GET/api/v1/ordersList lab and prescription ordersread:clinical
patientId: Only this patient.kind: Order kind.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "ord_7e6d5c4b3a2f1e0d9c8b",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"kind": "lab",
"detail": "Lipid panel",
"status": "drafted",
"createdAt": "2026-10-14T15:50:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}POST/api/v1/ordersDraft a lab or prescription orderwrite:drafts
Creates a draft only. A clinician approves it in Megvite. Sending a prescription is never available by API.
patientId (required): Patient the order is for.kind (required): Order kind.detail (required): What to order, 1 to 2000 characters.appointmentId: Appointment the order came from.{
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"kind": "lab",
"detail": "Lipid panel"
}{
"data": {
"id": "ord_7e6d5c4b3a2f1e0d9c8b",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"appointmentId": null,
"kind": "lab",
"detail": "Lipid panel",
"status": "drafted",
"createdAt": "2026-10-14T15:50:00.000Z"
}
}Queue
GET/api/v1/queueThe walk-in and waiting room queueread:schedule
Patients waiting now, oldest first.
status: Queue status.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "fdq_4c3b2a1f0e9d8c7b6a5f",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"status": "waiting",
"urgency": "routine",
"assignedProviderId": null,
"joinedAt": "2026-10-14T14:58:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Invoices
GET/api/v1/invoicesList patient invoicesread:analytics
Read only. Taking a payment is never available by API.
patientId: Only this patient.status: Invoice status.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "inv_9a8b7c6d5e4f3a2b1c0d",
"number": "INV-1042",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"description": "Follow-up visit",
"amountCents": 12000,
"paidCents": 0,
"status": "open",
"dueAt": "2026-11-01",
"createdAt": "2026-10-14T16:10:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Analytics
GET/api/v1/analytics/summaryClinic analytics summaryread:analytics
days: Window in days, 1 to 365. Default 30.{
"data": {
"windowDays": 30,
"from": "2026-09-11T00:00:00.000Z",
"to": "2026-10-11T00:00:00.000Z",
"patients": {
"total": 412,
"new": 37
},
"appointments": {
"scheduled": 58,
"completed": 211,
"cancelled": 9
},
"notes": {
"signed": 198
},
...eConsults
GET/api/v1/econsultsList eConsultsread:clinical
Read only: status and timing of written eConsults for a work queue. The question, files and reply stay in Megvite, and replying is never available by API.
patientId: Only this patient.status: eConsult status.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "ec_4c1d9e7a2b8f3e6d0a11",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"subject": "Rash on forearm",
"status": "answered",
"files": 2,
"createdAt": "2026-10-11T09:12:00.000Z",
"repliedAt": "2026-10-11T13:40:00.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Tasks
GET/api/v1/tasksList visit tasksread:clinical
Tasks from visits, newest change first. Default status is active (drafts and open tasks). Each task drafted by Megvite carries the exact words from the visit it came from; a due date appears only when one was stated.
status: Task status.patientId: Only this patient.appointmentId: Only tasks from this visit.limit: Items per page, 1 to 100. Default 25.cursor: The nextCursor value from the previous page.{
"data": [
{
"id": "vtk_3b9f1c0d7e2a4f6b8c1d",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"sessionId": "ses_1c9e2f7a40b3d8e6f001",
"visitAt": "2026-10-14T15:00:00.000Z",
"text": "Send the lipid panel order",
"ownerRole": "nurse",
"assigneeMemberId": null,
"assigneeName": null,
"dueDate": "2026-10-21",
"priority": "normal",
"status": "open",
"origin": "agent",
"source": {
"segmentId": "art_7d2e9c1b0a4f",
...POST/api/v1/tasks/{id}/completeComplete a taskwrite:drafts
Marks one open task done as the credential's member. A Megvite draft can be completed only by a clinician. Send an Idempotency-Key header.
id (required): Task id.{
"data": {
"changed": [
"vtk_3b9f1c0d7e2a4f6b8c1d"
],
"skipped": []
}
}POST/api/v1/tasks/completeComplete up to 50 taskswrite:drafts
Marks up to 50 open tasks done. The response lists which changed and which were skipped, with the reason. Send an Idempotency-Key header.
taskIds (required): 1 to 50 task ids.{
"taskIds": [
"vtk_3b9f1c0d7e2a4f6b8c1d"
]
}{
"data": {
"changed": [
"vtk_3b9f1c0d7e2a4f6b8c1d"
],
"skipped": []
}
}Search
GET/api/v1/searchSearch the visit recordread:clinical
Full-text search over saved visit transcripts, notes and call summaries, best match first, up to 50 passages. Scoped to the credential's clinic and the member's current access.
q (required): Words to find.patientId: Only this patient.tagId: Only visits with this clinic tag.kinds: Comma-separated sources: transcript, note, note_draft, summary.limit: Passages to return, 1 to 50. Default 25.{
"data": [
{
"id": "transcript:trn_9a1c",
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"patientId": "pat_8f2c41d0a7b94e1c55aa",
"source": "transcript",
"sourceId": "trn_9a1c",
"speaker": "Patient",
"stamp": "04:12",
"passage": "The tightness comes on after I climb the stairs",
"visitAt": "2026-10-14T15:00:00.000Z",
"imported": false
}
],
"hasMore": false,
"nextCursor": null
}GET/api/v1/visit-tagsList clinic visit tagsread:clinical
The clinic's visit tags, active and retired, with how many visits carry each one.
{
"data": [
{
"id": "tag_000001",
"label": "Medication review",
"status": "active",
"visitCount": 12
}
],
"hasMore": false,
"nextCursor": null
}POST/api/v1/appointments/{id}/tagsTag or untag a visitwrite:drafts
Adds a clinic tag to a visit or takes it off, as the credential's member (owners, admins and clinicians). A visit keeps up to 7 tags; a retired tag cannot be added. Tagging never signs, orders or sends anything. Send an Idempotency-Key header.
id (required): Appointment id.tagId (required): Clinic tag id from GET /api/v1/visit-tags.action: add (default) or remove.{
"tagId": "tag_000001",
"action": "add"
}{
"data": {
"appointmentId": "ses_1c9e2f7a40b3d8e6f001",
"tags": [
{
"id": "tag_000001",
"label": "Medication review",
"source": "clinician",
"addedAt": "2026-10-26T14:00:00.000Z"
}
],
"limit": 7
}
}Webhooks
Register an HTTPS endpoint in Settings and choose its events. Megvite posts each event when the change commits, signs it, and retries failures with backoff for about a day. The delivery log shows every attempt and lets you send one again.
- appointment.created
- An appointment was booked.
- appointment.updated
- An appointment changed time or status.
- appointment.cancelled
- An appointment was cancelled.
- visit.started
- A visit went live.
- visit.ended
- A visit ended.
- note.signed
- A clinician signed a visit note.
- message.received
- A patient sent a message.
- patient.created
- A patient was registered.
- queue.joined
- A patient joined the waiting room queue.
- rpm.alert
- A remote monitoring reading crossed a threshold.
{
"id": "evt_a558377f8227406da1ca",
"type": "appointment.created",
"createdAt": "2026-10-11T20:05:46.522Z",
"clinicId": "org_cua",
"data": {
"entity": { "type": "session", "id": "ses_40ecd91de160438aba98" },
"ids": { "patientId": "pat_6779b52e98824077a246" },
"state": "scheduled",
"counts": {}
}
}import crypto from 'node:crypto';
// rawBody: the request body exactly as received, before JSON.parse.
export function verifyMegvite(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ''));
}How every call behaves
Pagination
Lists return data, hasMore and nextCursor. Pass nextCursor back as cursor for the next page. limit runs from 1 to 100.
Errors
Every error is { "error": { "code", "message" } } with a matching HTTP status: 400, 401, 403, 404, 409 or 429.
Idempotency
Send an Idempotency-Key on every POST. A retry with the same key and body returns the first response with Idempotent-Replayed: true. The same key with a new body returns 409.
Rate limits
Each key or connection gets 120 requests a minute. Past that, calls return 429 with Retry-After.
Audit
Every call, allowed or refused, is written to the clinic audit log with the key, route and record.
Privacy
Psychotherapy notes and records flagged under 42 CFR Part 2 are never returned. Signing notes, sending prescriptions and taking payments stay inside Megvite.
Get an API key
A clinic owner or admin creates keys and webhook endpoints in Settings.