Comprehensive Guide: Multi-Payer Billing Configuration (BPJS, Insurance, General) in SIMRS
N
Back to Blog

Comprehensive Guide: Multi-Payer Billing Configuration (BPJS, Insurance, General) in SIMRS

Tutorial
Nugroho Setiawan · Konsultan IT & Full Stack Developer 04 Aug 2026 16 min baca 3,529 kata 310 views
Managing multi-payer billing in hospitals or clinics is a complex challenge. This article provides an in-depth guide to the strategies, architecture, and technical implementation for an efficient billing system integrated with BPJS, private insurance, and general patients, and compliant with SatuSeh

In the modern healthcare service ecosystem, facilities such as hospitals and clinics are faced with the complexity of billing management that is no joke. The biggest challenge is accommodating various payment schemes: from BPJS Kesehatan with its myriad regulations and dynamic bridging, private insurance with diverse policies and unique claim processes, to general patients with self-payment schemes. Without an integrated and intelligent system, the risk of errors, operational inefficiencies, and potential financial losses is enormous. Imagine a scenario where billing staff must manually verify BPJS eligibility, match insurance benefits, and calculate general bills separately; this is a recipe for chaos. This article serves as a comprehensive guide for hospital IT Managers, clinic owners, and SIMRS practitioners in designing and implementing a robust, efficient, and standards-compliant multi-payer billing system. We will discuss basic concepts, technical architecture, code implementation examples, error handling, and best practices that you can directly apply to optimize your billing operations.

Basic Concepts and Challenges of Multi-Payer Billing

Managing multi-payer billing is not just about adding up bills. It involves a deep understanding of various payer entities, their respective rules, and their impact on internal workflows. Broadly speaking, we are dealing with three main categories: BPJS Kesehatan, Private Insurance, and General Patients. Each category has very different characteristics in terms of membership verification, tariff structures, claim processes, and payment mechanisms.

For BPJS Kesehatan, healthcare facilities must follow regulations set by the government, such as Minister of Health Regulation (PMK) Number 52 of 2016 concerning Health Service Tariff Standards in the Implementation of the Health Insurance Program, and PMK Number 3 of 2023 concerning Health Service Tariff Standards in the Implementation of the Health Insurance Program. Membership verification is done through the VClaim or P-Care system, and claim calculation uses the INA-CBG system. The challenge is ensuring patient and service data is accurate, compliant with BPJS claim procedures, and submitted on time to avoid claim rejection (pending claim) or payment delay. Diagnosis and procedure data must be coded with correct ICD-10 and ICD-9-CM standards, often requiring trained medical coders.

Private insurance, on the other hand, offers greater diversity. Each insurance company (for example, Prudential, AXA Mandiri, Allianz, Mandiri Inhealth, BNI Life) has insurance products with different benefits, limits, and claim procedures. Some use a cashless system with online verification through bridging to a TPA (Third Party Administrator) such as AdMedika or Fuller Life, while others may require a manual or hybrid reimbursement process. The key to success here is the system's ability to dynamically manage the list of policies, benefits, and limits per patient, and to distinguish between services covered and not covered by insurance. For example, a patient with policy A might be covered for inpatient care up to Rp 100 million per year, while policy B only Rp 50 million and does not cover aesthetic services.

General patients, although seemingly the simplest, also have their own challenges. Setting tariffs for general patients often becomes a benchmark, but there needs to be flexibility for discounts or special service packages. The system must be able to accommodate cash, debit, credit payments, and digital payment methods such as QRIS or virtual accounts. Integration with cashier systems (Point of Sales) and accounting is crucial for accurate financial reporting. In addition, there is potential for general patients who later switch to insurance or BPJS schemes if their medical condition qualifies.

Data integration is the core of an effective multi-payer system. Patient registration data, medical history, services received (medications, procedures, rooms), diagnoses, and procedures must flow smoothly between SIMRS modules and external systems. An error at one point can result in claim rejection, payment delays, and patient dissatisfaction. Therefore, the system architecture must be designed with high modularity and robust integration capabilities, following standards such as FHIR R4 for health data interoperability.

System Architecture and Technical Implementation

Building a reliable multi-payer billing system requires a structured architecture and the use of appropriate technology. The main focus is on modularity, scalability, and integration capabilities. We recommend a microservices-based or modular monolithic architecture to separate billing business logic from other modules such as registration, medical records, or pharmacy. Relational databases such as PostgreSQL 16 LTS are a strong choice for storing patient data, services, tariffs, and payer configurations, thanks to data consistency and solid transaction support.

On the backend layer, using frameworks such as Laravel 11.x (PHP) or Node.js 20 LTS (Express.js/NestJS) is highly recommended. Laravel, with its rich ecosystem and Eloquent ORM, simplifies complex data management. Node.js offers high performance for I/O-bound operations common in external API integrations. For BPJS integration, the VClaim and P-Care API modules must be implemented. VClaim API version 2.0 allows membership verification, SEP (Surat Eligibilitas Peserta) creation, and data updates. P-Care API version 1.0 is used for primary care. Make sure you use a valid SSL certificate and follow strict security protocols according to BPJS guidelines.

Integration with private insurance often involves bridging to a TPA. For example, AdMedika provides a RESTful JSON-based API for benefit verification and claim submission. Your system needs to store configuration for each insurance: list of policies, covered benefits (for example, outpatient, inpatient, medication, procedures), exclusion list, and nominal limits. This can be implemented in the relational tables payers, insurance_plans, benefits, and patient_insurance_details that are related to one another. When a patient registers, the system will verify their policy against the services to be received.

For healthcare data interoperability, the FHIR (Fast Healthcare Interoperability Resources) Release 4 (R4) standard is key. Although it has not yet been fully implemented for billing in Indonesia directly, FHIR adoption will facilitate the exchange of medical data between your SIMRS and the SatuSehat platform or other systems in the future. You can use libraries such as HAPI FHIR version 6.8.x for Java or fhirclient for Python to parse and validate FHIR resources. Data such as Encounter, ServiceRequest, MedicationRequest, and Claim are relevant for billing. Transforming data from the internal SIMRS format to FHIR R4 becomes a crucial step.

Tariff management is a vital component. The system must be able to store various types of tariffs: BPJS tariffs (based on INA-CBG or top-up tariffs), insurance tariffs (often following general tariffs with special discounts), and general tariffs. This can be implemented with a tariffs table that has columns payer_type (BPJS, Insurance, General), service_id, amount, and valid_from/valid_until. The billing business logic will then select the appropriate tariff based on the patient's payer type at that time. Automating discount calculations or tariff adjustments based on contracts with private insurance will greatly reduce manual workload.

Code Implementation Example: Database Structure and Billing API

To provide a concrete illustration, let us look at an example database structure and API code snippet for multi-payer billing management. We will use PostgreSQL 16 and assume a backend based on Laravel 11.x.

-- Struktur Tabel Pasien
CREATE TABLE patients (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    mr_number VARCHAR(20) UNIQUE NOT NULL,
    full_name VARCHAR(255) NOT NULL,
    date_of_birth DATE NOT NULL,
    gender VARCHAR(10) NOT NULL,
    address TEXT,
    phone_number VARCHAR(20),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Struktur Tabel Payer (BPJS, Asuransi Swasta, Umum)
CREATE TABLE payers (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) UNIQUE NOT NULL,
    type VARCHAR(50) NOT NULL, -- 'BPJS', 'Insurance', 'General'
    config JSONB, -- Untuk menyimpan konfigurasi spesifik seperti API keys, endpoint, dll.
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Struktur Tabel Tarif Layanan
CREATE TABLE service_tariffs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    service_code VARCHAR(50) NOT NULL,
    service_name VARCHAR(255) NOT NULL,
    payer_id UUID REFERENCES payers(id),
    amount NUMERIC(15, 2) NOT NULL,
    valid_from DATE NOT NULL,
    valid_until DATE,
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (service_code, payer_id, valid_from)
);
-- Struktur Tabel Kunjungan/Encounter
CREATE TABLE encounters (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    patient_id UUID REFERENCES patients(id),
    payer_id UUID REFERENCES payers(id),
    encounter_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    encounter_type VARCHAR(50) NOT NULL, -- 'Rawat Jalan', 'Rawat Inap', 'UGD'
    status VARCHAR(50) NOT NULL DEFAULT 'Active',
    sep_number VARCHAR(100), -- Untuk BPJS
    policy_number VARCHAR(100), -- Untuk Asuransi
    total_bill NUMERIC(15, 2) DEFAULT 0.00,
    total_paid NUMERIC(15, 2) DEFAULT 0.00,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Struktur Tabel Detail Layanan dalam Kunjungan (Billing Items)
CREATE TABLE encounter_services (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    encounter_id UUID REFERENCES encounters(id),
    service_tariff_id UUID REFERENCES service_tariffs(id),
    quantity INTEGER NOT NULL DEFAULT 1,
    unit_price NUMERIC(15, 2) NOT NULL,
    sub_total NUMERIC(15, 2) NOT NULL,
    notes TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

The table structure above is designed to separate master data for patients, payers, and tariffs from visit and service transaction data. The payers table stores basic information about the payer entity, including type and specific configuration (for example, BPJS API credentials or insurance TPA endpoints). The service_tariffs table is the core of the multi-payer system, allowing you to define different tariffs for the same service depending on the payer type. The valid_from and valid_until columns are important for tariff version management.

// Contoh Endpoint API Laravel untuk Menghitung Tagihan Kunjungan// app/Http/Controllers/BillingController.phpnamespace App\Http\Controllers;use App\Models\Encounter;use App\Models\Payer;use App\Models\ServiceTariff;use Illuminate\Http\Request;use Illuminate\Support\Facades\DB;class BillingController extends Controller{\n    public function calculateEncounterBill(Request $request, string $encounterId)\n    {\n        $encounter = Encounter::with(['patient', 'payer', 'encounterServices.serviceTariff'])->findOrFail($encounterId);\n        if ($encounter->status !== 'Active') {\n            return response()->json(['message' => 'Encounter is not active for billing calculation.'], 400);\n        }\n        $totalBill = 0;\n        $payer = $encounter->payer;\n        foreach ($encounter->encounterServices as $encounterService) {\n            // Logika pemilihan tarif berdasarkan Payer\n            $tariff = ServiceTariff::where('service_code', $encounterService->serviceTariff->service_code)\n                                    ->where('payer_id', $payer->id)\n                                    ->whereDate('valid_from', '<=', $encounter->encounter_date)\n                                    ->where(function($query) use ($encounter) {\n                                        $query->whereNull('valid_until')\n                                              ->orWhereDate('valid_until', '>=', $encounter->encounter_date);\n                                    })\n                                    ->first();\n            if (!$tariff) {\n                // Jika tarif spesifik payer tidak ditemukan, fallback ke tarif umum atau error\n                $generalTariff = ServiceTariff::where('service_code', $encounterService->serviceTariff->service_code)\n                                            ->whereHas('payer', function ($q) {\n                                                $q->where('type', 'General');\n                                            })\n                                            ->first();\n                $unitPrice = $generalTariff ? $generalTariff->amount : 0; // Atau throw error jika tidak ada tarif\n                // Logika diskon asuransi jika menggunakan tarif umum\n                if ($payer->type === 'Insurance' && $payer->config && $payer->config['discount_percentage']) {\n                    $unitPrice *= (1 - ($payer->config['discount_percentage'] / 100));\n                }\n            } else {\n                $unitPrice = $tariff->amount;\n            }\n            $subTotal = $unitPrice * $encounterService->quantity;\n            $encounterService->update(['unit_price' => $unitPrice, 'sub_total' => $subTotal]);\n            $totalBill += $subTotal;\n        }\n        $encounter->update(['total_bill' => $totalBill]);\n        return response()->json([\n            'message' => 'Billing calculated successfully.',\n            'encounter' => $encounter->load('encounterServices')\n        ]);\n    }\n    // ... (fungsi lain untuk bridging BPJS, klaim asuransi, dll.)}

The API code above shows how billing calculation logic can be adjusted based on the payer_id of the Encounter. When a service is added to a patient visit, the system will look for the appropriate tariff in the service_tariffs table based on the patient's service_code and payer_id. If no specific tariff for that payer is found, the system can fall back to a general tariff or another mechanism, such as a special discount for insurance that uses the general tariff. This approach ensures flexibility and accuracy in billing calculations.

Payload Example and Error Handling

In the context of integration with external systems such as BPJS or insurance TPAs, data exchange often involves JSON or XML payloads. Understanding the payload structure and how to handle potential errors is crucial.

{\n    \"request\": {\n        \"t_sep\": {\n            \"noKartu\": \"0001234567890\",\n            \"tglSep\": \"2023-10-26\",\n            \"ppkPelayanan\": \"0301R001\",\n            \"jnsPelayanan\": \"1\",\n            \"klsRawat\": {\n                \"klsRawatHak\": \"3\",\n                \"klsRawatNaik\": \"\",\n                \"pembiayaan\": \"\",\n                \"penanggungJawab\": \"\"\n            },\n            \"noMR\": \"MR0012345\",\n            \"rujukan\": {\n                \"asalRujukan\": \"1\",\n                \"tglRujukan\": \"2023-10-25\",\n                \"noRujukan\": \"0301R0010923V000001\",\n                \"ppkRujukan\": \"0301R001\"\n            },\n            \"catatan\": \"Pasien dengan keluhan demam tinggi\",\n            \"diagAwal\": \"A09\",\n            \"poli\": {\n                \"tujuan\": \"INT\",\n                \"eksekutif\": \"0\"\n            },\n            \"cob\": {\n                \"cob\": \"0\"\n            },\n            \"katarak\": {\n                \"katarak\": \"0\"\n            },\n            \"jaminan\": {\n                \"lakaLantas\": \"0\",\n                \"noLP\": \"\",\n                \"tglKejadian\": \"\",\n                \"keterangan\": \"\",\n                \"suplesi\": {\n                    \"suplesi\": \"0\",\n                    \"noSepSuplesi\": \"\",\n                    \"lokasiLaka\": {\n                        \"kdPropinsi\": \"\",\n                        \"kdKabupaten\": \"\",\n                        \"kdKecamatan\": \"\"\n                    }\n                }\n            },\n            \"tglKunjungan\": \"2023-10-26\",\n            \"flagProcedure\": \"\",\n            \"kdPenunjang\": \"\",\n            \"assesmentPelayanan\": \"1\",\n            \"skdp\": {\n                \"noSurat\": \"SKDP001\",\n                \"kodeDPJP\": \"DR001\"\n            },\n            \"dpjpLayan\": \"DR001\",\n            \"noTelp\": \"08123456789\",\n            \"user\": \"Nugroho Setiawan\"\n        }\n    }}

The example above is a JSON payload for creating a SEP (Surat Eligibilitas Peserta) through the VClaim API of BPJS Kesehatan. Each field has specific validation and format requirements that must be met. Even the smallest error, such as an incorrect date format or an unregistered ppkPelayanan, can cause rejection.

Example error message from BPJS VClaim API:

{    "metaData": {        "code": "201",        "message": "Data tidak valid, tanggal rujukan tidak boleh lebih dari 90 hari"    }}

This error indicates that the referral date sent exceeds the 90-day limit set by BPJS. Handling errors like this requires strict data validation on your SIMRS side before sending requests to the external API. For this case, the system must have validation rules that check the difference between the referral date and the visit date.

Error Handling Strategies:

  1. Client Input Validation: Perform data validation as comprehensively as possible on the frontend (if any) and backend before sending requests to the external API. This reduces the load on the external system and speeds up feedback to users. For example, validate the format of noKartu, tglSep, ppkPelayanan according to BPJS specifications.
  2. Detailed Logging: Every request and response from the external API must be logged in detail, including the payload sent, the response received, and the timestamp. This is very helpful for debugging and auditing. Use a centralized logging system such as ELK Stack or Grafana Loki.
  3. Retry Mechanism: For temporary errors (for example, timeout or server overloaded), implement a retry mechanism with exponential backoff. Don't fail immediately; try again after a few seconds, then a few dozen seconds, etc.
  4. Admin Notification: If a critical or recurring error occurs, the system must be able to send automatic notifications to IT admins or the operations team via email, SMS, or Slack/Telegram.
  5. Clear Error Messages: Translate technical error messages into messages that are easy for non-technical users to understand. For example, "Invalid data, referral date cannot be more than 90 days" can be displayed as "The referral date exceeds the 90-day limit. Please recheck the referral date or contact BPJS officer."
  6. Fallback Strategy: Consider the worst-case scenario. If the BPJS or insurance API cannot be accessed, is there a temporary manual process that can be done? The system must have the ability to record transactions offline and sync them back when the connection is restored.
  7. API Monitoring: Use API monitoring tools (for example, Postman Monitors, New Relic, Datadog) to proactively monitor the availability and performance of external APIs.

Best Practices in Multi-Payer Billing Configuration

Implementing an effective multi-payer billing system is not only about code, but also about best practices that ensure sustainability and compliance.

  1. Understand Current Regulations: Always stay updated with the latest regulations from BPJS Kesehatan (for example, the latest PMK on tariffs, procedures), the Ministry of Health (especially regarding SatuSehat and data interoperability), and insurance company policies. These regulations often change, and compliance is key to avoiding claim rejections.
  2. Standardization of Service Codes: Apply standardization for service, drug, and diagnosis codes (ICD-10, ICD-9-CM, SNOMED CT if possible). This is crucial for data consistency, accurate reporting, and integration with external systems such as INA-CBG or SatuSehat.
  3. Flexible Database Design: Design a database schema that can accommodate variations in billing rules, tariffs, and configurations for each payer without requiring major schema changes. Using a JSONB column in PostgreSQL to store payer-specific configurations is an example of a good approach.
  4. Automated Eligibility Verification: Automate the BPJS membership verification process through the VClaim API and insurance benefit verification through the TPA API at the start of patient registration. This will reduce manual work, minimize errors, and speed up the service process.
  5. Version-Based Tariff Management: Implement a tariff management system that supports versioning (with valid_from and valid_until) for each service type and payer. This allows you to manage tariff changes historically and ensure correct calculations on the service date.
  6. Strong Audit Trail System: Every change to billing data, claims, or payment status must be recorded in an audit trail. This is important for accountability, dispute resolution, and meeting regulatory requirements. Record who made the change, when, and what change occurred.
  7. Continuous Staff Training: Ensure billing and front office staff receive continuous training on the new billing system, the latest regulations, and how to handle complex billing scenarios. Staff knowledge is an invaluable asset in maintaining operational efficiency.
  8. Comprehensive Integration Testing: Perform extensive integration testing with the BPJS API (using their development/staging environment) and insurance TPAs. Simulate various scenarios, including success cases, error cases, and edge cases to ensure the system functions as it should.
  9. Proactive Monitoring and Alerts: Implement a monitoring system that monitors external API performance, claim queue status, and potential billing anomalies. Set up automatic alerts for critical issues that require quick intervention.

FAQ about Multi-Payer Billing Configuration

The following are some common questions related to multi-payer billing configuration that often arise in healthcare facility environments.

  1. How to handle patients who have BPJS and also private insurance (COB - Coordination of Benefit)?

    Handling COB requires special logic in the billing system. Generally, BPJS will be the first payer according to PMK No. 76 of 2016. The system must calculate the BPJS claim first using INA-CBG, then the remaining bill not covered by BPJS can be submitted to private insurance as the second payer. Ensure there is a special field in the patient data to flag if the patient has COB and manage two claim processes in parallel.

  2. What is the biggest challenge in BPJS VClaim API integration?

    The main challenge is understanding the detailed and frequently changing API specifications, handling various errors, and ensuring data consistency between SIMRS and the BPJS system. Strict data validation before requests, comprehensive logging, and retry mechanisms are key to overcoming these challenges. API access security must also be considered, using authentication provided by BPJS.

  3. How to manage frequent tariff changes?

    The system must be designed with version-based tariff management, as explained in Section 3 with the valid_from and valid_until columns. When there is a tariff change (for example, a PMK update or renegotiation with insurance), you simply add a new tariff entry with a new effective start date, without deleting the old tariff. This enables historical tracking and accurate bill calculation based on the service date.

  4. How important is the adoption of the FHIR standard for billing?

    Although FHIR is not yet fully a mandatory standard for billing in Indonesia today, adopting FHIR R4 is very important for general health data interoperability. With SatuSehat as a national platform, patient medical data will be exchanged in FHIR format. Having the ability to convert your billing data into FHIR resources such as Claim or ExplanationOfBenefit will facilitate future integration and prepare your SIMRS for a broader digital health ecosystem.

  5. How to ensure patient data security in the billing system?

    Data security is the top priority. Implement data encryption in transit (HTTPS/TLS 1.2+) and at rest (disk encryption). Limit access to sensitive data based on role (Role-Based Access Control - RBAC). Conduct regular security audits, patch the system with the latest updates, and ensure compliance with data privacy regulations such as Law No. 27 of 2022 on Personal Data Protection.

  6. Is it necessary to develop your own billing module or use a third-party solution?

    This decision depends on your resources, budget, and the complexity of your needs. Developing it yourself provides full control and maximum customization, but requires a reliable developer team. Using a third-party solution can be faster and save initial costs, but you may be limited to the features provided. For complex multi-payer systems, a combination of both (using a third-party core module and extending it with custom integration) is often the best approach.

An effective multi-payer billing configuration is the financial backbone of every healthcare facility. It is not merely an administrative task, but an operational strategy that requires a blend of regulatory understanding, solid system architecture, and precise technical implementation. By following this guide, you will not only reduce the potential for errors and inefficiencies, but also build a strong foundation for the growth and sustainability of your healthcare services in the future. If you face challenges in integrating your billing system with BPJS, private insurance, or want to optimize your operational workflow with a tailored SIMRS solution, do not hesitate to contact us. The Nugroho Setiawan team is ready to help you design and implement the right technology solution for the unique needs of your healthcare facility, from consultation to full-stack development. Schedule a free consultation session now to discuss how we can help you achieve higher billing efficiency.

This article is part of the education by Nugroho Setiawan, an IT consultant in Jogja. Related solutions: SIMRS BPJS & SatuSehat Bridging Integrator. Need a free consultation?

Terakhir diperbarui 18 Oct 2026

Komentar

Komentar ditinjau sebelum tampil.

Belum ada komentar. Jadilah yang pertama!

Butuh bantuan implementasi?

Saya membantu konsultasi IT & pengembangan aplikasi untuk rumah sakit, klinik, dan bisnis di Yogyakarta (DIY), Magelang, dan sekitarnya.

Produk & Solusi SIMRS ERP Portfolio Konsultasi Gratis