---
title: "ABDM API — Error Handling Reference"
description: "How the Nice HMS ABDM integration APIs report errors: the JSON error envelope, the stable error-code table (code → HTTP → meaning), the per-route error reference, and the x-request-id correlation header."
date: 2026-08-26
lastModified: 2026-08-26
category: "developer"
author: "Dr. Umesh Bilagi"
beta: true
---

## 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.

```json
{
  "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**

```json
{
  "message": "Patient not found",
  "error": {
    "code": "PATIENT_NOT_FOUND",
    "message": "Patient not found"
  }
}
```

---

## Request correlation — `x-request-id` header

Every response includes an **`x-request-id`** header:

- 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.

---

## Error code taxonomy

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. |
| `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 | No open encounter, or the supplied `encounterId` was not found. |
| `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. |
| `DB_ERROR` | 500 | A database operation failed. Curated generic message. |
| `INTERNAL_ERROR` | 500 | Any unhandled / unknown error. Curated generic message. |

---

## HTTP status code summary

| Status | Category |
|---|---|
| 200 | Success |
| 400 | Bad request / validation |
| 401 | Unauthorized |
| 403 | Forbidden (subscription) |
| 404 | Not found |
| 409 | Conflict (duplicate) |
| 500 | Internal server error |
| 502 | Bad 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`.

---

## 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.)

| 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) · `ABDM_GATEWAY_ERROR` (502) · `LINK_TOKEN_MISSING` (502) |
| `/diagnostic_report` | `INVALID_DOCTOR_GCP_FHIR_ID` (400) · `PATIENT_NOT_FOUND` (404) · `ENCOUNTER_NOT_FOUND` (404) · `ABDM_GATEWAY_ERROR` (502) · `LINK_TOKEN_MISSING` (502) |
| `/op_consultation` | `INVALID_DOCTOR_GCP_FHIR_ID` (400) · `PATIENT_NOT_FOUND` (404) · `ENCOUNTER_NOT_FOUND` (404) · `ABDM_GATEWAY_ERROR` (502) · `LINK_TOKEN_MISSING` (502) |
| `/procedure_ot_notes` | `INVALID_DOCTOR_GCP_FHIR_ID` (400) · `PATIENT_NOT_FOUND` (404) · `ENCOUNTER_NOT_FOUND` (404) · `ABDM_GATEWAY_ERROR` (502) · `LINK_TOKEN_MISSING` (502) |
| `/health_document` | `INVALID_REQUEST_METHOD` (400) · `DOCTOR_DETAILS_REQUIRED` (400) · `INVALID_DOCTOR_GCP_FHIR_ID` (400) · `PATIENT_NOT_FOUND` (404) · `ENCOUNTER_NOT_FOUND` (404) · `ABDM_GATEWAY_ERROR` (502) · `LINK_TOKEN_MISSING` (502) |

---

## 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 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`) |
| Wrong `doctorGcpId` | `400 INVALID_DOCTOR_GCP_FHIR_ID` |
| Unknown `patientId` / `abhaAddress` | `404 PATIENT_NOT_FOUND` |
| Unknown `encounterId` | `404 ENCOUNTER_NOT_FOUND` |
| Duplicate email on `create_doctor` | `409 EMAIL_ALREADY_EXISTS` |
| ABDM OTP/search/verify failure | `502 ABDM_GATEWAY_ERROR` |
| ABDM link token missing after callback | `502 LINK_TOKEN_MISSING` |
| Anything else unexpected | `500 INTERNAL_ERROR` / `500 DB_ERROR` |
