Last updated: 4 October 2026
All endpoints are called with an Authorization: Bearer <idToken> header — except
/auth_token, which issues that token.
https://asia-south1-psychic-city-328609.cloudfunctions.net/<endpoint>
/discharge_patient is PUT./health_document is multipart/form-data (POST).Every error response uses the same JSON shape. There are no other error shapes.
{
"message": "Human-readable summary of the failure",
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{ "field": "patient.abhaAddress", "message": "The direct input of Abha Address or Abha number is not possible." }
]
}
}
| Field | Always present? | Meaning |
|---|---|---|
message | Yes | Top-level summary (kept for backward compatibility — safe to keep parsing it). |
error.code | Yes | Stable, machine-readable code. Match on this. |
error.message | Yes | Curated, human-readable message. |
error.details | No — only on VALIDATION_ERROR | Array of { field, message }, one entry per failed field. |
Example — non-validation error
{
"message": "Patient not found",
"error": {
"code": "PATIENT_NOT_FOUND",
"message": "Patient not found"
}
}
Every response — success or error — includes the same headers.
x-request-idx-request-id header, its value is echoed back unchanged.Send an x-request-id on every request and log the request/response pair against it. When
escalating an issue to Nice HMS support, include this value — it correlates to the
server-side log of the full (unredacted) error.
Access-Control-Allow-OriginEvery response includes Access-Control-Allow-Origin: *, so the API can be called
directly from browser-based clients (CORS is enabled for all origins). No per-origin
allow-list is required on the integrator's side.
Match on error.code — these are stable strings.
error.code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | Request body failed schema validation. See error.details for field-level reasons. Also used for an unsupported upload type, a missing file, or a PDF combined with images on health_document. |
VALIDATION_ERROR | 413 | An uploaded file exceeds the 5 MB limit (health_document). |
INVALID_EMAIL_FORMAT | 400 | An email string is malformed. |
INVALID_DOCTOR_GCP_FHIR_ID | 400 | A doctorGcpId does not exist in the FHIR store (Practitioner). |
DOCTOR_DETAILS_REQUIRED | 400 | doctorDetails is missing (health_document). |
INVALID_REQUEST_METHOD | 400 | Wrong HTTP method for the endpoint. |
UNAUTHORIZED | 401 | Missing/invalid/expired token, or the token is not mapped to an organization. |
SUBSCRIPTION_EXPIRED | 403 | The organization's subscription is invalid or expired. |
PATIENT_NOT_FOUND | 404 | No patient matches the supplied patientId / abhaAddress for this organization. |
ENCOUNTER_NOT_FOUND | 404 | A supplied encounterId was not found for this patient/organization. |
NO_ACTIVE_ENCOUNTER | 409 | A document endpoint resolved no encounter: the patient has no encounter at all (not even a finished one). Create a visit with opd_patient / admit_patient first. |
DOCTOR_NOT_FOUND | 404 | The Practitioner FHIR id does not exist (edit_doctor). |
EMAIL_ALREADY_EXISTS | 409 | Duplicate email when creating a doctor. |
ABDM_GATEWAY_ERROR | 502 | Upstream ABDM gateway returned an error (e.g. OTP verification failed, auth mode unavailable). |
LINK_TOKEN_MISSING | 502 | ABDM care-context link token was not found after the callback — a genuine failure, not a rate limit. |
LINK_TOKEN_RATE_LIMITED | 429 | ABDM refused to issue a link token because its cap of 3 new link tokens per ABHA address per 24 hours was reached. Reuse the existing token (valid ~6 months) and retry after 24 hours. |
DB_ERROR | 500 | A database operation failed. Curated generic message. |
INTERNAL_ERROR | 500 | Any unhandled / unknown error. Curated generic message. |
| Status | Category |
|---|---|
| 200 | Success |
| 400 | Bad request / validation |
| 401 | Unauthorized |
| 403 | Forbidden (subscription) |
| 404 | Not found |
| 409 | Conflict (duplicate email / no active encounter) |
| 413 | Payload too large (uploaded file exceeds the 5 MB limit) |
| 429 | Rate limited (ABDM link-token daily cap reached) |
| 500 | Internal server error |
| 502 | Bad gateway (ABDM upstream) |
400 VALIDATION_ERROR first (auth is checked after body
validation).details[].field use dot notation, e.g. patient.firstName,
doctorDetails.0.doctorGcpId, patient.dob.x-request-id.Some VALIDATION_ERROR messages name the rejected field but not its accepted values —
e.g. details[].message is just "Invalid category". The accepted values for each enum
field are listed in that endpoint's request table; see
ABDM API — Documents.
For example, /diagnostic_report category accepts:
Hematology·Biochemistry·Microbiology·Radiology·Others
The following codes are present on every authenticated route:
VALIDATION_ERROR(400) ·UNAUTHORIZED(401) ·SUBSCRIPTION_EXPIRED(403) ·DB_ERROR(500) ·INTERNAL_ERROR(500)
Each endpoint's documentation lists these shared codes + its own additional codes.
Where a route says "patientNeeds", the body must include at least one of abhaAddress
(string) or patientId (number). Omitting both yields 400 VALIDATION_ERROR with the
message "Either abhaAddress or patientId is required".
The table lists each route's additional codes — the ones on top of the shared codes.
(/auth_token is public and skips the subscription check.)
| Endpoint | Additional error codes |
|---|---|
/auth_token (public) | VALIDATION_ERROR (400) · UNAUTHORIZED (401) |
/create_patient | — |
/create_doctor | INVALID_EMAIL_FORMAT (400) · EMAIL_ALREADY_EXISTS (409) |
/edit_doctor | DOCTOR_NOT_FOUND (404) |
/get_doctor_opd_patients_by_date | — |
/get_doctor_ipd_patients_by_date | — |
/patient_by_id | PATIENT_NOT_FOUND (404) |
/patient_by_abhaAddress | PATIENT_NOT_FOUND (404) |
/patient_register_abha_otp_init | ABDM_GATEWAY_ERROR (502) |
/patient_register_abha_otp_confirm | ABDM_GATEWAY_ERROR (502) |
/opd_patient | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) |
/admit_patient | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) |
/discharge_patient | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) |
/discharge_summary | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) · NO_ACTIVE_ENCOUNTER (409) · ABDM_GATEWAY_ERROR (502) · LINK_TOKEN_MISSING (502) · LINK_TOKEN_RATE_LIMITED (429) |
/diagnostic_report | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) · NO_ACTIVE_ENCOUNTER (409) · ABDM_GATEWAY_ERROR (502) · LINK_TOKEN_MISSING (502) · LINK_TOKEN_RATE_LIMITED (429) |
/op_consultation | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) · NO_ACTIVE_ENCOUNTER (409) · ABDM_GATEWAY_ERROR (502) · LINK_TOKEN_MISSING (502) · LINK_TOKEN_RATE_LIMITED (429) |
/procedure_ot_notes | INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) · NO_ACTIVE_ENCOUNTER (409) · ABDM_GATEWAY_ERROR (502) · LINK_TOKEN_MISSING (502) · LINK_TOKEN_RATE_LIMITED (429) |
/health_document | INVALID_REQUEST_METHOD (400) · DOCTOR_DETAILS_REQUIRED (400) · INVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404) · NO_ACTIVE_ENCOUNTER (409) · ABDM_GATEWAY_ERROR (502) · LINK_TOKEN_MISSING (502) · LINK_TOKEN_RATE_LIMITED (429) |
/uhi and /uhi/whatsapp follow the UHI protocol (search/init/confirm/cancel/status),
which has its own acknowledgement and error format — documented separately, not via this
envelope.| You did this | You'll see |
|---|---|
| Missing/invalid token | 401 UNAUTHORIZED |
| Expired subscription | 403 SUBSCRIPTION_EXPIRED |
| Bad field / wrong enum / missing required field | 400 VALIDATION_ERROR (+ details) |
A file over 5 MB on health_document | 413 VALIDATION_ERROR |
An unsupported file type, no file, or a PDF combined with images on health_document | 400 VALIDATION_ERROR |
A file on health_document you expected to reach ABDM, but status isn't final | 200, with abdmNotified: false |
Wrong doctorGcpId | 400 INVALID_DOCTOR_GCP_FHIR_ID |
Unknown patientId / abhaAddress | 404 PATIENT_NOT_FOUND |
Unknown encounterId, or one belonging to another patient/org | 404 ENCOUNTER_NOT_FOUND |
| A document endpoint where the patient has no encounter at all (not even finished) | 409 NO_ACTIVE_ENCOUNTER |
Duplicate email on create_doctor | 409 EMAIL_ALREADY_EXISTS |
ABDM OTP/search/verify failure — including patient_register_abha_otp_init (ABHA address search / OTP request) and patient_register_abha_otp_confirm (OTP verification) | 502 ABDM_GATEWAY_ERROR (its error.message is ABDM's own reason) |
| ABDM link token missing after callback (genuine failure) | 502 LINK_TOKEN_MISSING |
| A 4th+ link-token generate for the same ABHA within 24 hours | 429 LINK_TOKEN_RATE_LIMITED |
| Anything else unexpected | 500 INTERNAL_ERROR / 500 DB_ERROR |
PATIENT_NOT_FOUND (404)PATIENT_NOT_FOUND means no patient matched the identifier you sent for the organization
tied to your bearer token. Common causes, in order of likelihood:
patientId sent as a string, not a number. patientId must be a JSON number —
1250, not "1250". A quoted value can fail the lookup even when the digits look right.abhaAddress is a placeholder, not a real address. A literal value like
"{{abhaAddress}}" (an unresolved template variable) matches no patient. Send the actual
ABHA address (e.g. savitribilagi@sbx), or omit abhaAddress entirely when using
patientId./create_patient) has an empty abhaAddress — it can only be fetched by patientId,
not by ABHA address. Conversely an ABHA patient is fetched by abhaAddress.abhaAddress can't mask a valid patientId.Confirm the identifier before creating a visit by fetching it with /patient_by_id or
/patient_by_abhaAddress. If it resolves there but a visit endpoint still returns
PATIENT_NOT_FOUND, escalate with the x-request-id — the server log holds the full
unredacted cause.
LINK_TOKEN_RATE_LIMITED 429 / LINK_TOKEN_MISSING 502)Document endpoints file a care context against an ABDM link token before pushing. ABDM issues at most 3 new link tokens per ABHA address per 24 hours; a token is valid about 6 months and supports unlimited document sends. So a token should be generated once and reused, not re-created per document.
| What you see | What it means | What to do |
|---|---|---|
429 LINK_TOKEN_RATE_LIMITED | The 3-per-24 h cap was reached for this ABHA. ABDM refused to issue another token. | Do not retry within the same 24 h window. Reuse the existing token — if you hold one, send it; otherwise wait and retry after 24 h. |
502 LINK_TOKEN_MISSING | No token was found after the generateToken callback. A genuine failure (or a callback that had not yet landed). | Retry once after a short delay; if it persists, escalate with the x-request-id. |
Guidance:
429 is not a payload problem. Nothing in your request body caused it, so changing
fields won't help.Timestamp fields use ISO 8601 — e.g. "2023-05-08T19:21:23.918Z" (see the examples in
each endpoint). patient.dob is the exception: YYYY-MM-DD (date only, no time).
| Field | Endpoint(s) | Required |
|---|---|---|
date | /opd_patient, /discharge_summary, /op_consultation, /procedure_ot_notes | Yes |
date | /diagnostic_report, /health_document | No (defaults to request time) |
doa | /admit_patient | Yes |
dod | /discharge_patient | Yes |
patient.dob | /create_patient | Yes |
A VALIDATION_ERROR of { "field": "date", "message": "Required" } means you hit one of
the endpoints in the first row and omitted date.