Sarva HealthPMS APIv1.0.0

Sarva PMS Integration API

Read and write a practice's schedule, patients, billing and work from your own system.

Tokenhttps://core-api.sarvahealth.ai/oauth/token
API basehttps://pms-api.sarvahealth.ai/integrations/v1

Getting started

The API is for systems. Sarva gives you a client id and secret. A practice then installs your system in the Sarva Console, choosing what it may do. Nothing works on a practice until then.

curl -X POST https://core-api.sarvahealth.ai/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d audience=pms-integrations

A token lasts 10 minutes and names what your client may do, not which practices. Name the practice on every request:

curl https://pms-api.sarvahealth.ai/integrations/v1/appointments?service_date=2026-11-02 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Sarva-Practice-Id: $PRACTICE_ID"

Answers and errors

Success is {"data": ..., "meta": {"request_id": ...}}. Failure is application/problem+json with a stable code.

StatusMeans
401The token is missing, expired or not for this API.
403The token, or the practice's consent, does not include this scope.
404It does not exist, or your system is not installed on this practice.
409It conflicts with the current state.
412, 428Send the current version in If-Match.
422The request is not valid. Fix it before retrying.
429Rate limited. Wait for Retry-After.

Retrying safely

Send a unique Idempotency-Key on every write, and reuse it to retry. A repeat with the same body returns the first response, and a different body under the same key is refused. Registering a webhook endpoint and rotating its secret take no key, because a secret is never stored for replay. POST /payments is also protected by your processor reference: that money posts once, whatever the key. POST /voice/outcomes is keyed by call and outcome.

Pagination

Lists return meta.page.next_cursor when there is more. Pass it as cursor.

Rate limits

Per client, practice and class of work. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset.

ClassBurstSustained
Reads60010 a second
Writes1202 a second
Eligibility301 every 2 seconds

Webhooks

Register an HTTPS endpoint with POST /webhook-endpoints. The signing secret is shown once. You receive an event only while your installation holds its scope.

Verify every delivery. Sarva-Signature is t=<unix>,v1=<hex>, an HMAC-SHA256 of t.body. While a secret rotates there are two v1 values.

import hashlib, hmac, time

def verify(header: str, secret: str, body: bytes) -> bool:
    parts = [p.partition("=") for p in header.split(",")]
    stamp = next(v for k, _, v in parts if k == "t")
    if abs(time.time() - int(stamp)) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{stamp}.".encode() + body,
                        hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, v) for k, _, v in parts if k == "v1")

Versioning

Within v1, changes only add. A breaking change is v2, announced with Deprecation and Sunset headers and six months' notice. Ignore fields you do not know.

Scopes

ScopeAllows
practice:readRead the practice: name, timezone, locations, providers
appointments:readRead appointments and availability
appointments:writeBook, reschedule, cancel and confirm appointments
patients:readRead patients and their coverages
patients:writeCreate and update patients and coverages
eligibility:readRead eligibility checks
eligibility:writeRequest eligibility checks
charges:readRead charges
charges:writeCreate charges
claims:readRead claims
claims:status:writeReport a payer's answer about a claim (any channel)
payments:readRead payments
payments:writePost a patient payment with a processor reference (posted once)
balances:readRead patient balances
balances:writeRecord payment plans and promises to pay
statements:readRead patient statements
remittances:writeSubmit remittance files (835)
documents:readRead documents
documents:writeSubmit documents
work_items:readRead work items
work_items:writeCreate and resolve work items
voice:outcomes:writeReport voice call outcomes
webhooks:manageManage webhook endpoints and redeliver events
events:readRead the event log for reconciliation

Events

API version 2026-11-01.

TypeNeedsFields
appointment.createdappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
appointment.rescheduledappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
appointment.cancelledappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
appointment.confirmedappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
appointment.checked_inappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
appointment.no_showappointments:readid, status, starts_at, ends_at, patient_id, provider_id, location_id
patient.createdpatients:readid, status, merged_into_id
patient.updatedpatients:readid, status, merged_into_id
patient.mergedpatients:readid, status, merged_into_id
coverage.createdpatients:readid, patient_id, status
coverage.endedpatients:readid, patient_id, status
eligibility_check.completedeligibility:readid, patient_id, status
charge.createdcharges:readid, patient_id, status, amount
charge.releasedcharges:readid, patient_id, status, amount
claim.submittedclaims:readid, patient_id, status, amount
claim.rejectedclaims:readid, patient_id, status, amount
claim.acknowledgedclaims:readid, patient_id, status, amount
claim.status_updatedclaims:readid, patient_id, status, amount
claim.adjudicatedclaims:readid, patient_id, status, amount
claim.deniedclaims:readid, patient_id, status, amount
payment.postedpayments:readid, patient_id, status, amount
remittance.postedpayments:readid, status, amount
refund.issuedpayments:readid, patient_id, status, amount
statement.sentstatements:readid, patient_id, status, amount
payment_plan.createdbalances:readid, patient_id, status, amount
payment_plan.behindbalances:readid, patient_id, status, amount
balance.changedbalances:readid, patient_id, amount
work_item.createdwork_items:readid, status, work_item_type
work_item.assignedwork_items:readid, status, work_item_type
work_item.resolvedwork_items:readid, status, work_item_type
document.receiveddocuments:readid, patient_id, status
document.fileddocuments:readid, patient_id, status
webhook.testwebhooks:manageid

Appointments

get/appointments

One day's schedule, cancelled and missed visits included

Scope appointments:read

ParameterInType
service_datequerydate
provider_idqueryuuid
location_idqueryuuid
Answers 200401403404422429500

post/appointments

Book an appointment

Scope appointments:write

FieldTypeRequired
ends_atdate-timerequired
external_appointment_idstringoptional
location_iduuidoptional
patient_iduuidrequired
provider_iduuidoptional
starts_atdate-timerequired
Answers 201401403404409422429500

get/appointments/{appointment_id}

One appointment

Scope appointments:read

ParameterInType
appointment_idpathuuid
Answers 200401403404422429500

post/appointments/{appointment_id}:cancel

Cancel a visit, saying whose decision it was

Scope appointments:write

ParameterInType
appointment_idpathuuid
FieldTypeRequired
reasonpatient_cancelled | practice_cancelled | provider_unavailable | closurerequired
Answers 200401403404409422429500

post/appointments/{appointment_id}:confirm

Record that the patient confirmed

Scope appointments:write

ParameterInType
appointment_idpathuuid
Answers 200401403404409422429500

post/appointments/{appointment_id}:reschedule

Move a visit: answers with the new appointment

A reschedule books a new appointment and cancels this one, together. The response is the **new** appointment, with its own id.

Scope appointments:write

ParameterInType
appointment_idpathuuid
FieldTypeRequired
ends_atdate-timerequired
location_iduuidoptional
provider_iduuidoptional
starts_atdate-timerequired
Answers 201401403404409422429500

get/availability

A provider's bookable slots on one day

Scope appointments:read

ParameterInType
provider_idqueryuuid
datequerydate
duration_minutesqueryinteger
location_idqueryuuid
Answers 200401403404422429500

Billing

get/balances

Patients who owe this practice, largest first

Scope balances:read

ParameterInType
min_balancequerynumber | string
limitqueryinteger
Answers 200401403404422429500

get/claims

Claims on this practice

Scope claims:read

ParameterInType
statequerystring
patient_idqueryuuid
claim_numberquerystring
limitqueryinteger
cursorquerystring
Answers 200401403404422429500

get/claims/{claim_id}

One claim

Scope claims:read

ParameterInType
claim_idpathuuid
Answers 200401403404422429500

post/claims/{claim_id}/status-reports

Report a payer's answer about a claim, from any channel

Normalised by lookup: a code the table does not hold reads as inconclusive and reaches a person. The claim is not moved: this is one party's account, recorded beside it.

Scope claims:status:write

ParameterInType
claim_idpathuuid
FieldTypeRequired
adjudicated_ondateoptional
channelphone | portal | edirequired
paid_amountnumber | stringoptional
requested_atdate-timerequired
responded_atdate-timeoptional
sourcestringrequired
status_category_codestringrequired
status_codestringoptional
Answers 201401403404409422429500

get/patients/{patient_id}/balance

What one patient owes, derived from the ledger

Scope balances:read

ParameterInType
patient_idpathuuid
Answers 200401403404422429500

post/payment-plans

Record a payment plan

Scope balances:write

FieldTypeRequired
currencystringoptional
first_due_datedaterequired
frequencyweekly | biweekly | monthlyrequired
instalment_amountnumber | stringrequired
notestringoptional
patient_iduuidrequired
total_amountnumber | stringrequired
Answers 201401403404409422429500

get/payments

Patient payments on this practice, newest first

Scope payments:read

ParameterInType
limitqueryinteger
cursorqueryuuid
Answers 200401403404422429500

post/payments

Record a patient payment a processor took; posted once

Scope payments:write

FieldTypeRequired
amountnumber | stringrequired
currencystringoptional
paid_ondateoptional
patient_iduuidrequired
processor_referencestringrequired
Answers 201401403404409422429500

post/promises

Record a promise to pay; a person checks it on the day

Scope balances:write

FieldTypeRequired
amountnumber | stringrequired
currencystringoptional
patient_iduuidrequired
promised_fordaterequired
Answers 201401403404409422429500

Records

get/charges

Charges on this practice

Scope charges:read

ParameterInType
statequerystring
encounter_idqueryuuid
limitqueryinteger
cursorquerystring
Answers 200401403404422429500

post/charges

Enter a charge on an encounter

The same price, checks and charge rules as the web API. Required modifiers are added.

Scope charges:write

FieldTypeRequired
diagnosis_codesarray of stringrequired
encounter_iduuidrequired
modifier_1stringoptional
modifier_2stringoptional
procedure_codestringrequired
procedure_code_setCPT | HCPCSoptional
unitsstringoptional
Answers 201401403404409422429500

post/documents

Submit a document. It arrives unfiled and a person places it

Scope documents:write

FieldTypeRequired
content_base64stringrequired
media_typestringrequired
Answers 201401403404409422429500

get/documents/{document_id}

One document's record; bytes are not served here

Scope documents:read

ParameterInType
document_idpathuuid
Answers 200401403404422429500

post/eligibility-checks

Ask the payer about a patient's coverage

Answers `202`. Read `GET /eligibility-checks/{id}`, or subscribe to `eligibility_check.completed`. A repeat with the same `Idempotency-Key` starts no second 270.

Scope eligibility:write

FieldTypeRequired
coverage_iduuidoptional
patient_iduuidrequired
service_datedateoptional
service_type_codestringoptional
Answers 202401403404422429500

get/eligibility-checks/{check_id}

One eligibility check and its answer

Scope eligibility:read

ParameterInType
check_idpathuuid
Answers 200401403404422429500

get/practice

This practice: name, timezone, locations, providers

Scope practice:read

Answers 200401403404422429500

post/remittances

Submit an 835. It is ingested; a person posts it

Scope remittances:write

FieldTypeRequired
content_base64stringrequired
Answers 201401403404409422429500

get/statements

Patient statements on this practice, newest first

Scope statements:read

ParameterInType
patient_idqueryuuid
limitqueryinteger
Answers 200401403404422429500

Webhooks

post/deliveries/{delivery_id}:redeliver

Queue this delivery again

Takes no `Idempotency-Key`: redelivering twice queues one delivery.

Scope webhooks:manage

ParameterInType
delivery_idpathuuid
Answers 202401403404422429500

get/webhook-endpoints

List this client's webhook endpoints on the practice

Scope webhooks:manage

Answers 200401403404422429500

post/webhook-endpoints

Register an endpoint; the signing secret is shown once

Takes no `Idempotency-Key`: a repeat answers `409 ALREADY_EXISTS`.

Scope webhooks:manage

FieldTypeRequired
descriptionstringoptional
event_typesarray of stringrequired
urlurirequired
Answers 201401403404409422429500

patch/webhook-endpoints/{endpoint_id}

Change an endpoint, or enable or disable it

Scope webhooks:manage

ParameterInType
endpoint_idpathuuid
FieldTypeRequired
descriptionstringoptional
event_typesarray of stringoptional
statusenabled | disabledoptional
urlurioptional
Answers 200401403404409422429500

delete/webhook-endpoints/{endpoint_id}

Delete an endpoint and its delivery log

Scope webhooks:manage

ParameterInType
endpoint_idpathuuid
Answers 204401403404422429500

get/webhook-endpoints/{endpoint_id}/deliveries

The delivery log: status, latency and response code, never a response body

Scope webhooks:manage

ParameterInType
endpoint_idpathuuid
limitqueryinteger
Answers 200401403404422429500

post/webhook-endpoints/{endpoint_id}:rotate-secret

A new signing secret; both sign for 24 hours

Takes no `Idempotency-Key`: a repeat rotates again, which is harmless.

Scope webhooks:manage

ParameterInType
endpoint_idpathuuid
Answers 200401403404422429500

post/webhook-endpoints/{endpoint_id}:send-test

Queue a `webhook.test` event to this endpoint

Scope webhooks:manage

ParameterInType
endpoint_idpathuuid
Answers 202401403404422429500

Events

get/events

Events on this practice, newest first, last 30 days

Scope events:read

ParameterInType
limitqueryinteger
typequerystring
sincequerydate-time
cursorquerystring
Answers 200401403404422429500

get/events/{event_id}

One event

Scope events:read

ParameterInType
event_idpathstring
Answers 200401403404422429500

Patients

get/patients

Find patients on this practice by name, MRN or external id

Scope patients:read

ParameterInType
namequerystring
mrnquerystring
external_patient_idquerystring
limitqueryinteger
cursorquerystring
Answers 200401403404422429500

post/patients

Register a patient

Scope patients:write

FieldTypeRequired
date_of_birthdaterequired
emailstringoptional
external_patient_idstringoptional
first_namestringrequired
last_namestringrequired
phonestringoptional
sex_at_birthM | F | Uoptional
Answers 201401403404409422429500

get/patients/{patient_id}

One patient

Scope patients:read

ParameterInType
patient_idpathuuid
Answers 200401403404422429500

patch/patients/{patient_id}

Correct a patient's contact details and name

`If-Match` is required: two systems must not overwrite each other.

Scope patients:write

ParameterInType
patient_idpathuuid
If-Matchheaderstring
FieldTypeRequired
emailstringoptional
first_namestringoptional
last_namestringoptional
phonestringoptional
Answers 200401403404409412422428429500

get/patients/{patient_id}/coverages

A patient's policies, primary first; a subscriber's date of birth is never returned

Scope patients:read

ParameterInType
patient_idpathuuid
Answers 200401403404422429500

post/patients/{patient_id}/coverages

Put a policy on a patient

Scope patients:write

ParameterInType
patient_idpathuuid
FieldTypeRequired
cob_rankintegerrequired
effective_fromdaterequired
effective_todateoptional
group_numberstringoptional
member_idstringrequired
payer_iduuidrequired
subscriber_date_of_birthdateoptional
subscriber_first_namestringoptional
subscriber_last_namestringoptional
subscriber_relationshipself | spouse | child | otherrequired
Answers 201401403404409422429500

Meta

get/ping

Is the Integration API reachable

Answers 200

Voice

post/voice/outcomes

Report what a call achieved; applied automatically where it can be

`disposition` says what happened: `applied`, or `work_item` with the `floor` that needed a person. A resend with the same `call_id` and `outcome` returns the first result (`200`).

Scope voice:outcomes:write

FieldTypeRequired
appointment_iduuidoptional
call_iduuidrequired
claim_numberstringoptional
notestringoptional
outcomeconfirmed | rescheduled | cancelled | paid | payment_plan | promise_to_pay | billing_question | callback_requested | wrong_number | opted_out | no_contact | payer_answerrequired
patient_iduuidoptional
paymentobjectoptional
planobjectoptional
promiseobjectoptional
rescheduleobjectoptional
Answers 201401403404422429500

Work items

get/work-items

Work items on this practice

Scope work_items:read

ParameterInType
statequerystring
work_item_typequerystring
limitqueryinteger
cursorquerystring
Answers 200401403404422429500

post/work-items

Ask a person at the practice to do something about a patient

Scope work_items:write

FieldTypeRequired
detailstringrequired
due_atdate-timeoptional
patient_iduuidrequired
titlestringrequired
Answers 201401403404422429500

post/work-items/{work_item_id}:resolve

Close a work item with a code

An item with a live governing obligation answers `OBLIGATION_UNRESOLVED`: closing over a live deadline stays with a person.

Scope work_items:write

ParameterInType
work_item_idpathuuid
FieldTypeRequired
notestringoptional
resolution_codeactioned | no_action_required | information_missing | duplicate | superseded | referred_externallyrequired
Answers 200401403404409422429500