API documentation
Machine access for approved companies: validate FHIR resources from your software, scripts and CI pipelines.
Base URL: https://fhir-sandbox.sahasratech.com.np · FHIR base: https://fhir-sandbox.sahasratech.com.np/fhir · FHIR R4 (4.0.1), NP Core 1.0.0
Authentication
Create an API key on the API keys page (approved companies only). Keys start with npfs_ and are shown once. Send the key in every request:
Authorization: Bearer npfs_xxxxxxxxxxxxxxxxxxxxxxxx
GET /fhir/metadata and GET /api/profiles are public. Everything else requires a key (or a signed-in browser session).
POST /fhir/$validate
Validates a FHIR resource (JSON or XML) against NP Core and returns a FHIR OperationOutcome. Nothing is stored. Choose the profile with ?profile=
curl -sS -X POST "https://fhir-sandbox.sahasratech.com.np/fhir/\$validate?profile=http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-patient" \ -H "Authorization: Bearer $NPFS_API_KEY" \ -H "Content-Type: application/fhir+json" \ --data-binary @patient.json
import os, requests
r = requests.post("https://fhir-sandbox.sahasratech.com.np/fhir/$validate",
params={"profile": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-patient"},
headers={"Authorization": f"Bearer {os.environ['NPFS_API_KEY']}",
"Content-Type": "application/fhir+json"},
data=open("patient.json", "rb"), timeout=120)
issues = r.json()["issue"]
errors = [i for i in issues if i["severity"] in ("error", "fatal")]
for i in issues:
print(i["severity"], i.get("expression"), i.get("diagnostics"))const res = await fetch("https://fhir-sandbox.sahasratech.com.np/fhir/$validate", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NPFS_API_KEY}`, "Content-Type": "application/fhir+json" },
body: await fs.promises.readFile("patient.json", "utf8"),
});
const oo = await res.json();
const errors = oo.issue.filter((i) => ["error", "fatal"].includes(i.severity));Each issue carries the raw validator text in details.text, a plain-language explanation and fix in diagnostics, the location in expression, and a structured explanation in the extension https://fhir-sandbox.mohp.gov.np/StructureDefinition/explanation. The resource conforms when there is no issue with severity error or fatal.
POST /api/validate
Returns the sandbox ValidationReport (the same data the web validator shows) as plain JSON.
POST /api/validate
Content-Type: application/json
{ "profile": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-patient", "resource": { "resourceType": "Patient", … } }
{
"id": "…run id…", "createdAt": "…", "valid": false,
"resourceType": "Patient", "profile": "…", "profileTitle": "NP Core Patient",
"summary": { "fatal": 0, "error": 1, "warning": 0, "information": 0 },
"issues": [ {
"severity": "error", "code": "structure", "messageId": "…",
"location": "Patient", "line": 1, "col": 2, "message": "raw validator text",
"explanation": { "title": "…", "what": "…", "why": "…", "fix": "…",
"example": "…", "igLink": "…", "category": "identifier" }
} ],
"operationOutcome": { "resourceType": "OperationOutcome", "issue": [ … ] }
}
Related: GET /api/profiles (catalog of NP Core profiles), GET /api/runs?limit=&cursor= and GET /api/runs/{id} (your history).
FHIR test server
Each approved company has its own private FHIR R4 test server built into the sandbox; a company only ever sees its own data. Writes are validated first: if there is any error, the request is rejected with 422 and the explained OperationOutcome, and nothing is stored.
JSON only: XML writes get 415 and _format=xml gets 406 (XML is fine for $validate).
| Request | Behaviour |
|---|---|
GET /fhir/metadata | CapabilityStatement (public); lists the NP Core profiles as supportedProfile |
GET /fhir/{type}/{id} | Read. 404 unknown, 410 deleted. ETag W/"version"; If-None-Match gives 304. |
GET /fhir/{type}/{id}/_history[/{vid}] | History (newest first, max 100) and vread |
POST /fhir/{type} | Create; server-assigned id. Validated and references checked first: 422 with explained OperationOutcome, or 201 with Location and ETag. Prefer: return=minimal | representation | OperationOutcome. |
PUT /fhir/{type}/{id} | Update, or create with your own id (201). Body id must equal the URL id (else 400). If-Match mismatch gives 412. |
DELETE /fhir/{type}/{id} | Soft delete, 204, idempotent; If-Match supported |
GET /fhir/{type}?…, POST /fhir/{type}/_search | Search — see the parameters below |
POST /fhir | transaction (all-or-nothing; order DELETE, POST, PUT, GET) or batch Bundle. urn:uuid fullUrls are resolved; every entry is validated. ifNoneExist and conditional requests give 400. |
POST /fhir/$validate, /fhir/{type}/$validate, /fhir/{type}/{id}/$validate | Raw resource or Parameters(resource, profile); always 200 with explained OperationOutcome |
Search parameters
- All types:
_id,identifier(system|valueorvalue),_lastUpdatedwitheq|gt|ge|lt|le(repeat for a range). patient(Patient/xorx) on types with a subject or patient;subject(Type/xorx) on types with a subject. Comma-separated values mean OR.- Result controls:
_count(default 20, max 100),_offset,_sort=_lastUpdatedor-_lastUpdated(default),_summary=count,_pretty=true. - Anything else — other parameters, modifiers such as
:exact, other_sort/_summaryvalues — returns 400 listing the supported parameters.
References
- Type/id must exist in your company's store or be created in the same Bundle, and the target type must be allowed by the NP Core profile (otherwise 422, e.g. SANDBOX_REFERENCE_TARGET_TYPE).
- Absolute URLs to this sandbox are normalised to Type/id; other absolute URLs are accepted with a warning; #contained references are allowed; conditional references are rejected.
Not supported
Whole-system search and history, type-level _history, PATCH (405), _format=xml (406), conditional create/update/delete.
Quota and reset
Each company may store a limited number of live resources; above it writes get 403. Use “Reset my test data” on the dashboard to wipe your company's test data. CORS is enabled on /fhir only.
Scenarios & reports
GET /api/scenarios | All scenarios with your latest status |
POST /api/scenarios/{id}/submit | Body {"resource": …} or {"bundle": …}; returns the result of each check |
GET /api/company/report | Conformance summary (per group pass %) |
Status codes
200 | OK ($validate always returns 200 with an OperationOutcome, valid or not) |
201 | Created (FHIR test server) |
204 / 304 | Deleted / not modified |
400 | Malformed request (e.g. no resource in the body) |
401 | Missing, wrong or revoked API key |
403 | Company not approved or suspended, or storage quota reached |
404 / 410 | Resource not found / deleted |
405 / 406 / 415 | PATCH not supported / _format=xml / XML body on a write |
412 | If-Match version conflict |
413 | Request too large |
422 | Write rejected because the resource is not conformant (body: explained OperationOutcome) |
429 | Rate limit exceeded — retry later with back-off |
503 | Validation engine temporarily unavailable — retry in a minute |
Limits
- Requests are rate-limited per API key; pending companies have lower limits in the web validator.
- Maximum body size: 5 MB. Maximum 500 entries per Bundle.
- Use a client timeout of at least 60 seconds.
- Use synthetic test data only. Never send real patient information.
Example: a conformant NP Core Patient
{
"resourceType": "Patient",
"id": "ExampleNPCorePatient",
"meta": {
"profile": [
"http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-patient"
]
},
"identifier": [
{
"use": "official",
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "NI",
"display": "National unique individual identifier"
}
]
},
"system": "http://donidcr.gov.np",
"value": "1234-5678-9012-3"
},
{
"use": "official",
"type": {
"coding": [
{
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/np-core-identifier-type",
"code": "health-id",
"display": "Nepal Health ID"
}
]
},
"system": "http://fhir.digitalhealth.gov.np/core/NamingSystem/nepal-health-id",
"value": "10000000001"
},
{
"use": "official",
"type": {
"coding": [
{
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/np-core-identifier-type",
"code": "citizenship-number",
"display": "Nepal Citizenship Certificate Number"
}
]
},
"system": "http://moha.gov.np",
"value": "12-34-56-78901"
},
{
"use": "official",
"type": {
"coding": [
{
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/np-core-identifier-type",
"code": "health-insurance-id",
"display": "Nepal Health Insurance ID"
}
]
},
"system": "http://hib.gov.np",
"value": "HIB-000123456"
},
{
"use": "official",
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "PPN",
"display": "Passport number"
}
]
},
"system": "http://nepalpassport.gov.np",
"value": "PA1234567"
},
{
"use": "official",
"type": {
"coding": [
{
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/np-core-identifier-type",
"code": "ssf-number",
"display": "Nepal Social Security Fund Number"
}
]
},
"system": "http://ssf.gov.np",
"value": "1-01-01-00001234"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "Shrestha",
"given": [
"Aarav"
]
}
],
"gender": "male",
"birthDate": "1990-05-15",
"_birthDate": {
"extension": [
{
"url": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-bikram-sambat-date",
"valueString": "2047-02-01"
}
]
},
"address": [
{
"extension": [
{
"url": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-province",
"valueCoding": {
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/nepal-administrative-location",
"code": "3",
"display": "Bagmati Province"
}
},
{
"url": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-district",
"valueCoding": {
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/nepal-administrative-location",
"code": "306",
"display": "Kathmandu"
}
},
{
"url": "http://fhir.digitalhealth.gov.np/core/StructureDefinition/np-core-local-level",
"valueCoding": {
"system": "http://fhir.digitalhealth.gov.np/core/CodeSystem/nepal-administrative-location",
"code": "30608",
"display": "Kathmandu Metropolitan City"
}
}
],
"use": "home",
"type": "physical",
"text": "Ward 10, Naxal, Kathmandu Metropolitan City, Kathmandu, Bagmati Province, Nepal",
"line": [
"Ward 10",
"Naxal"
],
"city": "Kathmandu Metropolitan City",
"district": "Kathmandu",
"state": "Bagmati Province",
"postalCode": "44600",
"country": "NP"
}
]
}