Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Krama Core

Python-first FHIR, compliance, and healthcare integration toolkit for country-adaptive clinical products.

Built by Nirvya Labs.

Krama Core helps developers build secure healthcare integrations across jurisdictions. It includes ABDM-compliant FHIR R4 bundles for India, generic FHIR builders, HIP/HIU flows, encryption, clinical templates, gateway resilience, WhatsApp messaging, AI-assisted clinical workflows, global patient identifiers, and country-aware compliance guardrails.

The architecture is intentionally layered:

  • Clinical domains define what care is documented: general medicine, dentistry, ophthalmology, pediatrics, psychiatry, surgery, Ayurveda, and more.
  • Country adapters define how that care must identify, protect, code, and exchange data: ABHA in India, IHI/MRN in Australia, MRN/MBI in the US, NHS Number/MRN in the UK, and custom/local identifiers elsewhere.
  • Compliance policies define what must be checked before data is processed: purpose, consent or lawful basis, encryption, residency, auditability, and minimum-necessary data sharing.

This keeps Krama India-ready without making it India-only.

CI PyPI version Python 3.10+ License: MIT

Installation

pip install krama-core

Optional integrations:

pip install "krama-core[ai]"
pip install "krama-core[whatsapp]"

For local development:

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Learning The Codebase

If you are new to Krama Core, start with the learning guide:

docs/KRAMA_LEARNING_GUIDE.md

It explains the architecture, what each module does, how FHIR, crypto, HIP/HIU, templates, adapters, and compliance connect, and which files to read first.

Quick Start

India ABDM Bundle

import json

from krama.fhir import create_op_consult_bundle
from krama.fhir.bundles import (
    DiagnosisInfo,
    OrganizationInfo,
    PatientInfo,
    PractitionerInfo,
)

bundle = create_op_consult_bundle(
    patient=PatientInfo(
        name="Ravi Kumar",
        abha_address="ravi.kumar@abdm",
        gender="male",
        date_of_birth="1990-05-15",
    ),
    practitioner=PractitionerInfo(
        name="Dr. Priya Sharma",
        identifier="DOC-12345",
    ),
    organization=OrganizationInfo(
        name="District Hospital Guntur",
        hfr_id="IN0410000123",
    ),
    diagnosis=DiagnosisInfo(
        description="Essential hypertension",
        snomed_code="59621000",
        clinical_notes="BP 150/95, prescribed amlodipine 5mg",
    ),
    encounter_date="2026-05-06",
)

print(json.dumps(bundle, indent=2))

Global Patient Identity

from krama.fhir.resources import FHIRPatient, PatientIdentifier

australia_patient = FHIRPatient(
    identifiers=[
        PatientIdentifier.australia_ihi("8003608166690503"),
        PatientIdentifier.australia_mrn("MRN-123", assigner="Royal Melbourne"),
    ],
    name="Amelia Brown",
    gender="female",
    birth_date="1988-04-12",
)

us_patient = FHIRPatient(
    identifiers=[
        PatientIdentifier.us_mrn("DENT-456", assigner="Smile Dental Boston"),
    ],
    name="Jordan Smith",
    gender="unknown",
    birth_date="1975-09-20",
)

Country Compliance Check

from krama.compliance import ComplianceContext, ComplianceEngine

result = ComplianceEngine().evaluate(
    ComplianceContext(
        country="AUS",
        purpose="Dental review",
        patient_identifiers=["australia_ihi"],
        consent_present=True,
        encrypted=True,
        data_residency_region="ap-southeast-2",
        requested_fields=["diagnosis", "medications"],
        necessary_fields=["diagnosis", "medications"],
        actor_id="dentist-1",
        audit_event_id="audit-1",
    )
)

assert result.passed

Supported Bundles

Krama Core supports three ABDM care contexts through convenience functions:

  • create_op_consult_bundle() for outpatient consultation records
  • create_prescription_bundle() for prescription records
  • create_discharge_summary_bundle() for inpatient discharge summaries

Each bundle is returned as a JSON-serializable Python dictionary.

FHIR Builder API

For richer workflows, Krama Core includes resource builders and fluent document builders:

from krama.fhir import OPConsultBuilder
from krama.fhir.resources import (
    FHIROrganization,
    FHIRPatient,
    FHIRPractitioner,
    PatientIdentifier,
)

bundle = (
    OPConsultBuilder()
    .set_patient(
        FHIRPatient(
            abha_id="ravi.kumar@abdm",
            name="Ravi Kumar",
            gender="male",
            birth_date="1990-05-15",
        )
    )
    .set_practitioner(
        FHIRPractitioner(identifier="DOC-12345", name="Dr. Priya Sharma")
    )
    .set_organization(
        FHIROrganization(hfr_id="IN0410000123", name="District Hospital Guntur")
    )
    .set_encounter("2026-05-06")
    .add_chief_complaint("Essential hypertension", snomed_code="59621000")
    .add_observation("8480-6", "Systolic blood pressure", 130, unit="mmHg")
    .add_medication("Amlodipine", "5mg daily", snomed_code="386864001")
    .build()
)

Patients can be identified by ABHA for India, national identifiers where a country has one, or local medical record numbers scoped to the assigning hospital/system:

india_patient = FHIRPatient(
    abha_id="ravi.kumar@abdm",
    name="Ravi Kumar",
    gender="male",
    birth_date="1990-05-15",
)

australia_patient = FHIRPatient(
    identifiers=[
        PatientIdentifier.australia_ihi("8003608166690503"),
        PatientIdentifier.australia_mrn("MRN-123", assigner="Royal Melbourne"),
    ],
    name="Amelia Brown",
    gender="female",
    birth_date="1988-04-12",
)

us_patient = FHIRPatient(
    identifiers=[
        PatientIdentifier.us_mrn("MRN-789", assigner="Mass General"),
    ],
    name="Jordan Smith",
    gender="unknown",
    birth_date="1975-09-20",
)

uk_patient = FHIRPatient(
    identifiers=[
        PatientIdentifier.uk_nhs_number("9000000009"),
        PatientIdentifier.uk_mrn("MRN-UK-1", assigner="Guy's and St Thomas'"),
    ],
    name="Ava Taylor",
    gender="female",
    birth_date="1992-11-03",
)

Local MRNs must include either a FHIR system URI or an assigner; Krama derives a stable local URN from the assigner to avoid collisions across hospitals.

The builder layer currently supports OPConsultBuilder and PrescriptionBuilder, plus reusable FHIR resources for Patient, Practitioner, Encounter, Condition, Observation, MedicationRequest, DiagnosticReport, AllergyIntolerance, Procedure, Organization, and Composition.

Encryption

Krama Core includes tested ECDH and AES-GCM helpers for secure health data transfer flows:

from krama.crypto import AESGCMCipher, ECDHKeyExchange

sender_private, sender_public = ECDHKeyExchange.generate_key_pair()
receiver_private, receiver_public = ECDHKeyExchange.generate_key_pair()

sender_secret = ECDHKeyExchange.derive_shared_secret(sender_private, receiver_public)
receiver_secret = ECDHKeyExchange.derive_shared_secret(receiver_private, sender_public)

sender_key = AESGCMCipher.derive_key(sender_secret)
receiver_key = AESGCMCipher.derive_key(receiver_secret)

ciphertext, nonce = AESGCMCipher.encrypt(b"clinical payload", sender_key)
plaintext = AESGCMCipher.decrypt(ciphertext, receiver_key, nonce)

SDK Client

Krama Core also includes a secure async client foundation for ABDM workflows:

from krama import KramaClient

async with KramaClient(
    client_id="your-client-id",
    client_secret="your-client-secret",
) as krama:
    result = await krama.abha.create_via_mobile("+91 98765 43210")
    print(result.transaction_id)

Security defaults:

  • Client secrets use Pydantic secret types and are redacted from repr/errors
  • Gateway URLs must use HTTPS unless targeting localhost for tests
  • Access tokens are cached and refreshed behind an async lock
  • Gateway errors avoid echoing request payloads or secrets
  • Tests use mock transports only; no real ABDM requests are made in CI

HIP And HIU

HIP discovery callbacks should acknowledge immediately and defer all work:

from krama.hip import DiscoveryHandler, DiscoveryMatch


async def find_care_contexts(request):
    return DiscoveryMatch(patient_abha=request.patient_abha, care_contexts=[])


handler = DiscoveryHandler(http_client=krama.http, processor=find_care_contexts)
ack = await handler.handle(callback_body)  # return this from your web handler

# Run outside the request path, for example in a background worker.
await handler.process_next()

Publishing validates the FHIR document bundle, creates and links a care context, then notifies the gateway:

await krama.hip.publish(
    patient_abha="ravi.kumar@abdm",
    bundle=bundle,
    care_context_reference="visit-2026-05-06",
    care_context_display="OP consultation, 6 May 2026",
)

HIU helpers cover consent, data requests, and encrypted payload receive/decrypt:

from krama.hiu import ConsentRequest, DataRequest

consent = await krama.hiu.consents.request_consent(
    ConsentRequest(
        patient_abha="ravi.kumar@abdm",
        purpose="Care management",
        hiu_id="nirvya-hiu",
        date_range_from="2026-01-01",
        date_range_to="2026-12-31",
    )
)

await krama.hiu.data_requests.request_data(DataRequest(consent_id=consent.consent_id))

Clinical Templates

Krama Core ships clinical form templates across 12 medical domains, so one SDK can support allopathy, dentistry, Ayurveda, homeopathy, surgery, pediatrics, ophthalmology, OB-GYN, psychiatry, dermatology, orthopedics, and ENT.

from krama.templates import TemplateRegistry

registry = TemplateRegistry()

template = registry.get("ayurveda", "prakriti_assessment")
print(template.name)
print([section.label for section in template.sections])

for domain in registry.list_domains():
    print(domain)

Custom templates can be registered with the same Pydantic models:

from krama.templates import ClinicalTemplate, TemplateSection

registry.register(
    ClinicalTemplate(
        domain="allopathy",
        encounter_type="followup",
        name="Follow-up Visit",
        description="Short follow-up visit template",
        sections=[
            TemplateSection(
                id="interval_history",
                label="Interval History",
                type="textarea",
                required=True,
            )
        ],
        vitals=["bp", "weight"],
        coding_system="icd10",
        prescription_type="standard",
    )
)

Universal Adaptive Template

For global products, Krama includes one robust template that adapts by country. It keeps the same clinical shape everywhere while changing identifier guidance, coding system, compliance frameworks, and residency metadata per jurisdiction:

from krama.templates import UniversalTemplateContext, create_universal_template

template = create_universal_template(UniversalTemplateContext(country="AU"))

print(template.jurisdiction)  # AUS
print(template.coding_system)  # icd10_am
print(template.metadata["identifier_types"])

Supported defaults are included for India, Australia, US, and UK. Unknown countries fall back to a conservative global template using local/custom identifiers. The same universal template can be paired with any domain-specific template: a dental clinic, ophthalmology clinic, or psychiatry practice can use the same country compliance engine without needing separate per-country clinical forms.

Gateway Resilience

Gateway calls can be wrapped with retry, circuit breaker, and health checks:

from krama.gateway import CircuitBreaker, RetryConfig, retry_gateway_call

breaker = CircuitBreaker()


@retry_gateway_call(RetryConfig(max_retries=3))
async def notify_gateway():
    return await krama.http.post("/v1/hip/health-information/notify", json={})


result = await breaker.execute(notify_gateway)
health = await krama.gateway_health.check()
print(health.connected, health.latency, health.last_successful_call)

Retries are limited to timeouts and 5xx responses. 4xx gateway responses are treated as client errors and are not retried.

WhatsApp

The WhatsApp module normalizes inbound messages and routes outbound sends through a configured provider:

from krama.whatsapp import TemplateMessage, WhatsAppSender
from krama.whatsapp.providers import MetaDirectProvider

provider = MetaDirectProvider(
    access_token="meta-token",
    phone_number_id="phone-number-id",
)
sender = WhatsAppSender(provider)

await sender.send_text("919876543210", "Your appointment is confirmed.")
await sender.send_template(
    "919876543210",
    TemplateMessage(
        template_name="appointment_reminder",
        params={"name": "Ravi", "date": "6 May"},
        language="en",
    ),
)

Supported providers: AiSensy, Gupshup, and Meta WhatsApp Cloud API. Webhooks are parsed into one InboundMessage schema regardless of provider.

AI

Clinical AI helpers are optional and provider-routed. All clinical outputs carry the same safety rule: AI output is a suggestion only and requires physician review.

from krama.ai import AIAssistant
from krama.ai.providers import GeminiProvider, GroqProvider, LLMRouter

router = LLMRouter(
    [
        GeminiProvider(api_key="gemini-key"),
        GroqProvider(api_key="groq-key"),
    ]
)
ai = AIAssistant(router)

suggestions = await ai.clinical_nlp.suggest_soap_improvement(
    "assessment",
    "Essential hypertension",
)
codes = await ai.icd_coder.suggest_codes("Essential hypertension")
triage = await ai.triage.classify_urgency("fever for three days")
drug_check = ai.drug_checker.check_interactions(
    medications=["Warfarin", "Aspirin"],
    patient_allergies=[],
)

The router tries providers in priority order and automatically fails over when a provider errors or returns an empty response.

Country Adapters

Country adapters give the SDK a stable surface for national health networks:

adapter = krama.adapter("IND")

identity = await adapter.verify_patient_identity(
    {"abha_number": "12345678901234"}
)
transaction_id = await adapter.publish_health_record(bundle)
consent = await adapter.request_consent("ravi.kumar@abdm", "Care management")

print(adapter.get_coding_system())
print(adapter.get_drug_formulary())
print(adapter.get_data_residency_region())
print(adapter.get_supported_patient_identifiers())

India delegates to Krama ABHA, HIP, and HIU modules. Australia, UK, and US adapters currently expose metadata and identifier preferences, and raise NotImplementedError for network operations until their national integrations are added.

Supported identifier preferences:

Country Preferred patient identifiers Coding Residency
India ABHA, ABHA address, local MRN ICD-10 ap-south-1
Australia IHI, local MRN, Medicare ICD-10-AM ap-southeast-2
US MRN, MBI, local MRN ICD-10-CM us-east-1
UK NHS Number, local MRN ICD-10 eu-west-2

This country-adapter layer is where Krama will grow toward country-specific healthcare rules: coding systems, formularies, consent laws, data residency, identity verification, and clinical/network compliance guidance.

Compliance Engine

Krama includes a conservative compliance guardrail engine. It does not replace legal review, but it gives product teams a common way to block unsafe workflows and surface warnings before records are signed, shared, published, or requested:

from krama.compliance import ComplianceContext

result = krama.compliance.evaluate(
    ComplianceContext(
        country="US",
        purpose="Referral",
        patient_identifiers=["us_mrn"],
        lawful_basis="treatment",
        encrypted=True,
        requested_fields=["diagnosis", "medications"],
        necessary_fields=["diagnosis", "medications"],
        actor_id="clinician-1",
        audit_event_id="audit-1",
    )
)

if not result.passed:
    print(result.blockers)

The first rule packs cover India, Australia, US, and UK. They check for patient identity, supported identifier type, purpose of use, consent or lawful basis, encryption, data residency, minimum-necessary data sharing, and auditability. This creates the platform foundation for deeper country healthcare guideline systems: each country can add its own rules without breaking the universal template.

The compliance engine is a software guardrail, not legal advice. Production teams should still complete legal, security, privacy, and clinical governance review for each deployment.

What Krama Handles

  • Bundle.type = "document"
  • Composition as the first bundle entry
  • Internal urn:uuid: references between resources
  • Patient, practitioner, organization, encounter, diagnosis, and medication resources
  • Country-aware patient identifiers
  • Universal adaptive clinical templates
  • Country-aware compliance blockers and warnings
  • SNOMED CT coding fields for diagnoses and medications
  • Pydantic validation for required inputs and FHIR gender values

Development

Run the checks:

pytest -v --cov=krama
ruff check src/ tests/ examples/
bandit -r src/
pip-audit
python examples/basic_usage.py

Build and validate package artifacts:

python -m build
twine check dist/*

Status

1.0.0-alpha. The SDK now covers the planned core layers, but provider-specific integrations and national adapters will keep evolving before a stable 1.0.0.

Roadmap

  • Local mock ABDM gateway for offline development
  • Async webhook handler with callback reliability helpers
  • Diagnostic Report and Immunization bundle types
  • FHIR R4 bundle validator
  • FastAPI integration examples

Why "Krama"?

Krama means order, sequence, or method. ABDM integration is a strict sequence of care contexts, callbacks, consent flows, and clinical records. Krama exists to make that sequence easier to build and reason about.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

krama_core-1.0.0a3.tar.gz (79.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

krama_core-1.0.0a3-py3-none-any.whl (89.0 kB view details)

Uploaded Python 3

File details

Details for the file krama_core-1.0.0a3.tar.gz.

File metadata

  • Download URL: krama_core-1.0.0a3.tar.gz
  • Upload date:
  • Size: 79.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for krama_core-1.0.0a3.tar.gz
Algorithm Hash digest
SHA256 9c0f3d7973dead2afdfd59d280055c9c1c036c631e3b5a456f1ded6cfd1d5bc3
MD5 2f623cee4a795bdbdba3f71c0360a3a1
BLAKE2b-256 b5a8462e1a325312e3d8446cb56e4bff9f23d760c62c7f11b783b578cf382d44

See more details on using hashes here.

File details

Details for the file krama_core-1.0.0a3-py3-none-any.whl.

File metadata

  • Download URL: krama_core-1.0.0a3-py3-none-any.whl
  • Upload date:
  • Size: 89.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for krama_core-1.0.0a3-py3-none-any.whl
Algorithm Hash digest
SHA256 40514397db920522b28f399658eed4f27502e052e29a79d8f534c424420b2b59
MD5 c166893b019d592839188e0e7f81acdc
BLAKE2b-256 6780c6b6ad24f53989fadf5a8790b37d5f84705bc9e54d6b5fb517a8aba7156e

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0a3 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page