ABDM API — Error Handling Reference

Beta

Last updated: 4 October 2026

Browse Documentation▼

Base URL

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>
  • Default method is POST.
  • /discharge_patient is PUT.
  • /health_document is multipart/form-data (POST).

Error envelope (every non-2xx response)

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." }
    ]
  }
}
FieldAlways present?Meaning
messageYesTop-level summary (kept for backward compatibility — safe to keep parsing it).
error.codeYesStable, machine-readable code. Match on this.
error.messageYesCurated, human-readable message.
error.detailsNo — only on VALIDATION_ERRORArray 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"
  }
}

Response headers

Every response — success or error — includes the same headers.

x-request-id

  • If your request included an x-request-id header, its value is echoed back unchanged.
  • Otherwise a new UUID is generated.
  • The value lives in the header only — never inside the JSON body.

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-Origin

Every 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.


Error code taxonomy

Match on error.code — these are stable strings.

error.codeHTTPMeaning
VALIDATION_ERROR400Request 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_ERROR413An uploaded file exceeds the 5 MB limit (health_document).
INVALID_EMAIL_FORMAT400An email string is malformed.
INVALID_DOCTOR_GCP_FHIR_ID400A doctorGcpId does not exist in the FHIR store (Practitioner).
DOCTOR_DETAILS_REQUIRED400doctorDetails is missing (health_document).
INVALID_REQUEST_METHOD400Wrong HTTP method for the endpoint.
UNAUTHORIZED401Missing/invalid/expired token, or the token is not mapped to an organization.
SUBSCRIPTION_EXPIRED403The organization's subscription is invalid or expired.
PATIENT_NOT_FOUND404No patient matches the supplied patientId / abhaAddress for this organization.
ENCOUNTER_NOT_FOUND404A supplied encounterId was not found for this patient/organization.
NO_ACTIVE_ENCOUNTER409A 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_FOUND404The Practitioner FHIR id does not exist (edit_doctor).
EMAIL_ALREADY_EXISTS409Duplicate email when creating a doctor.
ABDM_GATEWAY_ERROR502Upstream ABDM gateway returned an error (e.g. OTP verification failed, auth mode unavailable).
LINK_TOKEN_MISSING502ABDM care-context link token was not found after the callback — a genuine failure, not a rate limit.
LINK_TOKEN_RATE_LIMITED429ABDM 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_ERROR500A database operation failed. Curated generic message.
INTERNAL_ERROR500Any unhandled / unknown error. Curated generic message.

HTTP status code summary

StatusCategory
200Success
400Bad request / validation
401Unauthorized
403Forbidden (subscription)
404Not found
409Conflict (duplicate email / no active encounter)
413Payload too large (uploaded file exceeds the 5 MB limit)
429Rate limited (ABDM link-token daily cap reached)
500Internal server error
502Bad gateway (ABDM upstream)

Validation semantics (important)

  1. Extra / unknown fields are ignored (stripped), not rejected. You will not get an error for sending fields the API doesn't know about.
  2. Validation runs before auth. A request that is both unauthenticated and has an invalid body returns 400 VALIDATION_ERROR first (auth is checked after body validation).
  3. Field paths in details[].field use dot notation, e.g. patient.firstName, doctorDetails.0.doctorGcpId, patient.dob.
  4. Curated messages only. Responses never leak stack traces, SQL, DB error text, GCP FHIR paths/IDs, or raw ABDM payloads. The full unredacted cause is logged server-side against the x-request-id.

Field enum values

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


Shared error codes (every authenticated route)

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.


Per-route error reference

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.)

EndpointAdditional error codes
/auth_token (public)VALIDATION_ERROR (400) · UNAUTHORIZED (401)
/create_patient—
/create_doctorINVALID_EMAIL_FORMAT (400) · EMAIL_ALREADY_EXISTS (409)
/edit_doctorDOCTOR_NOT_FOUND (404)
/get_doctor_opd_patients_by_date—
/get_doctor_ipd_patients_by_date—
/patient_by_idPATIENT_NOT_FOUND (404)
/patient_by_abhaAddressPATIENT_NOT_FOUND (404)
/patient_register_abha_otp_initABDM_GATEWAY_ERROR (502)
/patient_register_abha_otp_confirmABDM_GATEWAY_ERROR (502)
/opd_patientINVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404)
/admit_patientINVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404)
/discharge_patientINVALID_DOCTOR_GCP_FHIR_ID (400) · PATIENT_NOT_FOUND (404) · ENCOUNTER_NOT_FOUND (404)
/discharge_summaryINVALID_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_reportINVALID_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_consultationINVALID_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_notesINVALID_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_documentINVALID_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)

Not covered here

  • /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.

Quick reference — which code for which mistake

You did thisYou'll see
Missing/invalid token401 UNAUTHORIZED
Expired subscription403 SUBSCRIPTION_EXPIRED
Bad field / wrong enum / missing required field400 VALIDATION_ERROR (+ details)
A file over 5 MB on health_document413 VALIDATION_ERROR
An unsupported file type, no file, or a PDF combined with images on health_document400 VALIDATION_ERROR
A file on health_document you expected to reach ABDM, but status isn't final200, with abdmNotified: false
Wrong doctorGcpId400 INVALID_DOCTOR_GCP_FHIR_ID
Unknown patientId / abhaAddress404 PATIENT_NOT_FOUND
Unknown encounterId, or one belonging to another patient/org404 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_doctor409 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 hours429 LINK_TOKEN_RATE_LIMITED
Anything else unexpected500 INTERNAL_ERROR / 500 DB_ERROR

Troubleshooting: 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:

  1. 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.
  2. 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.
  3. Wrong identifier for the patient type. A non-ABHA patient (created via /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.
  4. Organization scoping. A patient created under one organization is not visible to a different organization's token. Use the token of the org that registered the patient.
  5. Sending both identifiers. Validation accepts either, but the lookup precedence when both are supplied is undefined — send exactly one (the one you intend to match) so a bad 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.


Troubleshooting: the link token (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 seeWhat it meansWhat to do
429 LINK_TOKEN_RATE_LIMITEDThe 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_MISSINGNo 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:

  1. Generate once, reuse. A link token lasts ~6 months — generate it at patient/ABHA linking time and keep it, rather than requesting a new one for each document.
  2. Don't retry a 429 in a loop. Each retry counts against the same daily cap and keeps you blocked; wait out the 24 h window.
  3. A 429 is not a payload problem. Nothing in your request body caused it, so changing fields won't help.

Date and time fields

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).

FieldEndpoint(s)Required
date/opd_patient, /discharge_summary, /op_consultation, /procedure_ot_notesYes
date/diagnostic_report, /health_documentNo (defaults to request time)
doa/admit_patientYes
dod/discharge_patientYes
patient.dob/create_patientYes

A VALIDATION_ERROR of { "field": "date", "message": "Required" } means you hit one of the endpoints in the first row and omitted date.

Was this page helpful?
Index
NICE HMS. 1st Gate Nehru Stadium, City Hubballi, District Dhrawad, State Karnataka, 580020
INDIA, Phone : +919611560555 email admin@nicehms.com, GST 29AEYPB4702Q1ZS
facebook
twitter
linkedin
youtube
RSS