Get started
API reference

Every route a key can call.

66 endpoints, generated from the code that serves them, so this page cannot list a door that is shut. One base URL, bearer auth, JSON in and out.

  • Base URL https://medappz.com/v1
  • 10 scopes
  • 14 webhook events

Authentication

Two kinds of key, both made by a clinic admin inside the app and shown once.

API keys · mza_…Admin → Integrations. Each key holds named scopes and can call only the routes those scopes open. It reaches its own clinic and nothing else, expires within two years, and is limited to 600 requests a minute.
curl https://medappz.com/v1/patients/search?q=asha \
  -H "Authorization: Bearer mza_<prefix>_<secret>"
Device keys · mzv_…Voice Inbox → Integrations. For a recorder or notetaker: it can open, feed and close voice sessions and read what became of them, and nothing else.
curl -X POST https://medappz.com/v1/voice/ingest/sessions \
  -H "X-MedAppz-Key: mzv_<prefix>_<secret>" \
  -H "Content-Type: application/json" -d @session.json

Scopes

What a key can be given. Anything not on this list — billing, prescriptions, notes, HR — has no machine access.

lead.readlead.managepatient.readappointment.readquote.readsurvey.readadmitline.readimaging.readimaging.managebenchlink.post

Endpoints

Paths are under https://medappz.com/v1. Search by path, scope or what it does.

Leads and CRM · 17Push enquiries from your website, ad forms or call centre, and move them through the pipeline.
  • GET/v1/crm/leads/scoresScores: crm leads scoresScope: lead.read
  • GET/v1/crm/pipeline/reportReport: crm pipeline reportScope: lead.read· Query: from, source, to
  • GET/v1/leadsList: leadsScope: lead.read· Query: due, mine, source, status
  • POST/v1/leadsRecord an enquiry.Scope: lead.manage· JSON body
  • GET/v1/leads/:idDetail: leads (one record)Scope: lead.read
  • POST/v1/leads/:id/assignAssign: leads assign (one record)Scope: lead.manage· JSON body
  • POST/v1/leads/:id/bookedBooked: leads booked (one record)Scope: lead.manage· JSON body
  • POST/v1/leads/:id/contactedContacted: leads contacted (one record)Scope: lead.manage· JSON body
  • POST/v1/leads/:id/convertConvert: leads convert (one record)Scope: lead.manage· JSON body
  • POST/v1/leads/:id/lostLost: leads lost (one record)Scope: lead.manage· JSON body
  • POST/v1/leads/:id/nextNext: leads next (one record)Scope: lead.manage· JSON body
  • GET/v1/leads/sourcesWhere enquiries come from and what each source costs.Scope: lead.read· Query: from, to
  • GET/v1/leads/spendSpend: leads spendScope: lead.read
  • PUT/v1/leads/spendSet spend: leads spendScope: lead.manage· JSON body
  • GET/v1/quotesList: quotesScope: quote.read
  • GET/v1/surveysList: surveysScope: survey.read
  • GET/v1/surveys/summarySummary: surveys summaryScope: survey.read
Patients · 8Find a patient, read the record your key is allowed to read, and check for duplicates before you create one.
  • GET/v1/documents/opd-card/:patientId/pdfOpd card: documents opd card pdf (one record)Scope: patient.read
  • GET/v1/patients/:patientIdThe full record needs a care relationship, not just the permission. Holding `patient.read` lets you read *a* patient, never *this* one.Scope: patient.read
  • GET/v1/patients/:patientId/chartChart: patients chart (one record)Scope: patient.read
  • GET/v1/patients/by-mrn/:mrnBy mrn: patients by mrn (one record)Scope: patient.read
  • POST/v1/patients/duplicate-checkCalled as the clerk types, before anything is created.Scope: patient.read· JSON body
  • GET/v1/patients/searchSearch: patients searchScope: patient.read· Query: limit, q
  • GET/v1/portal-accessStatus: portal accessScope: patient.read· Query: patientId
  • GET/v1/registration-requestsList: registration requestsScope: patient.read· Query: status
Appointments and availability · 16Read diaries, free slots and bookings, for a booking widget, a call centre or a calendar.
  • GET/v1/appointments/availabilityAvailability: appointments availabilityScope: appointment.read· Query: from, schedulableId, serviceType, to
  • GET/v1/appointments/dayAppointment day: appointments dayScope: appointment.read· Query: date, schedulableId
  • GET/v1/booking-offersList: booking offersScope: appointment.read· Query: exceptionId, status
  • GET/v1/booking-offers/exceptions/:exceptionIdAffected: booking offers exceptions (one record)Scope: appointment.read
  • GET/v1/booking-policy/depositsDeposits: booking policy depositsScope: appointment.read· Query: status
  • GET/v1/booking-policy/lateLate: booking policy lateScope: appointment.read
  • GET/v1/booking-seriesList: booking seriesScope: appointment.read· Query: patientId, status
  • GET/v1/booking-series/:idGet: booking series (one record)Scope: appointment.read
  • POST/v1/booking-series/planPlan: booking series planScope: appointment.read· JSON body
  • GET/v1/encounters/:id/follow-up-slotsFollow up slots: encounters follow up slots (one record)Scope: appointment.read· Query: days, schedulableId
  • GET/v1/patients/:patientId/appointmentsPatient appointments: patients appointments (one record)Scope: appointment.read
  • GET/v1/scheduling/bookingsBookings: scheduling bookingsScope: appointment.read· Query: limit, subjectId, subjectType
  • GET/v1/scheduling/bookings/:groupId/cardOne appointment as a confirmation reads it: doctor, type, prep note, when.Scope: appointment.read
  • GET/v1/scheduling/bookings/:groupId/icsThe same appointment as a calendar file for a phone.Scope: appointment.read
  • GET/v1/scheduling/diaryThe calendar's read: a day or a week of bookings with the patient's name, the type and the state on each, for every resource at once.Scope: appointment.read· Query: facilityId, from, schedulableId, to
  • GET/v1/scheduling/schedulables/:id/dayOne resource's list for one day — the clinic's working view.Scope: appointment.read· Query: date
Admissions funnel · 5Read-only: report the admission pipeline to a referral partner or a dashboard.
  • GET/v1/admitline/boardBoard: admitline boardScope: admitline.read· Query: doctor, mine, owner, patientId, payer, q, stage
  • GET/v1/admitline/cases/:idDetail: admitline cases (one record)Scope: admitline.read
  • GET/v1/admitline/doctorsThe "advised by" picker. Names only — no privileges, no rota.Scope: admitline.read
  • GET/v1/admitline/funnelFunnel: admitline funnelScope: admitline.read· Query: by, from, to
  • GET/v1/admitline/summaryCounts of admission cases by stage, for a dashboard.Scope: admitline.read
Imaging bridge · 13For an imaging gateway on the hospital network: the modality worklist in, studies back out.
  • GET/v1/imaging/archivesArchives: imaging archivesScope: imaging.read
  • POST/v1/imaging/archives/checkTest the connection to each imaging archive and record the result.Scope: imaging.read
  • POST/v1/imaging/pollAsk the archive what it has now.Scope: imaging.manage
  • GET/v1/imaging/studiesStudies: imaging studiesScope: imaging.read· Query: patientId, state, worklistEntryId
  • POST/v1/imaging/studiesThe bridge's way in, and the manual one.Scope: imaging.manage· JSON body
  • POST/v1/imaging/studies/:id/discardDiscard: imaging studies discard (one record)Scope: imaging.manage· JSON body
  • POST/v1/imaging/studies/:id/matchMatch: imaging studies match (one record)Scope: imaging.manage· JSON body
  • POST/v1/imaging/studies/:id/refreshRefresh: imaging studies refresh (one record)Scope: imaging.manage
  • POST/v1/imaging/studies/:id/unmatchUnmatch: imaging studies unmatch (one record)Scope: imaging.manage· JSON body
  • GET/v1/imaging/studies/unmatchedStudies the archive holds that are not yet matched to a patient.Scope: imaging.read
  • GET/v1/imaging/worklistWorklist: imaging worklistScope: imaging.read· Query: from, patientId, state, station, to
  • POST/v1/imaging/worklistSchedule: imaging worklistScope: imaging.manage· JSON body
  • POST/v1/imaging/worklist/:id/stateSet state: imaging worklist state (one record)Scope: imaging.manage· JSON body
Lab analysers · 2For the BenchLink edge agent that speaks ASTM and HL7 v2 to the analysers on the bench.
  • POST/v1/benchlink/device/resultsPost an analyser’s results for a sample; they wait for verification.Scope: benchlink.post· JSON body
  • GET/v1/benchlink/device/worklistThe host query: which tests are ordered on this sample barcode.Scope: benchlink.post· Query: analyzerId, barcode
Voice ingest · 5For recorders, phone apps and meeting notetakers. Uses a device key (mzv_…), not an API key.
  • POST/v1/voice/ingest/sessionsOpen a consultation. Refused without the patient's consent; idempotent on `externalId`.Device key· JSON body· 120/min
  • GET/v1/voice/ingest/sessions/:idStatus and every proposal, with what became of it.Device key· 600/min
  • POST/v1/voice/ingest/sessions/:id/closeClose it: extraction starts straight away.Device key· 120/min
  • GET/v1/voice/ingest/sessions/:id/eventsServer-sent events: `proposal.created`, `proposal.accepted`, `proposal.rejected`, `session.extracted`, `session.blocked`.Device key· Query: after· 30/min
  • POST/v1/voice/ingest/sessions/:id/segmentsAppend up to 200 segments. A segment id already stored is counted as a duplicate, not an error.Device key· JSON body· 600/min

Webhook events

Signed with HMAC-SHA256 and retried with backoff. Envelopes carry ids and changed field names, never clinical content.

patient.patient.registeredpatient.patient.updatedpatient.patient.mergedscheduling.booking.createdscheduling.booking.cancelledclinical.encounter.finishedbilling.bill.finalisedbilling.payment.receivedbilling.bill.voidedbilling.payment.refundedwardline.admission.admittedwardline.admission.dischargedleaddesk.lead.createdadmitline.case.staged

Errors and retries

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "phone must be a valid mobile number.",
    "requestId": "req_01J…"
  }
}
Send an Idempotency-Key header on writes so a retry is a no-op, not a duplicate. A 429 means slow down; quote the requestId when you write to us. The developer guide walks through pushing a lead and verifying a webhook signature end to end.