If you're building payroll, HR, or ERP systems for Indonesian companies, BPJS integration is eventually unavoidable. BPJS Ketenagakerjaan (employment social security) and BPJS Kesehatan (health insurance) are mandatory for all formal Indonesian employees — which means any payroll system that handles Indonesian employee data needs to calculate BPJS contributions, report them, and ideally submit them programmatically. I've worked on BPJS integration at Commsult Indonesia, and the experience taught me that the official documentation and the real API behavior are different enough to warrant a practical guide.
There are two separate BPJS systems: BPJS Ketenagakerjaan (Labor BPJS, formerly Jamsostek) covers work accident insurance (JKK), death insurance (JKM), old age savings (JHT), and pension (JP). BPJS Kesehatan covers health insurance (JKN). Both have separate APIs, separate credentials, and separate integration requirements. For payroll integration, you primarily need BPJS Ketenagakerjaan's contribution calculation and reporting. For healthcare management systems (klinik, rumah sakit), BPJS Kesehatan's PCare and VClaim APIs are the relevant interfaces.
BPJS Ketenagakerjaan's API uses a REST-based architecture with HMAC-SHA256 signature authentication. You need: a Consumer ID (Cons ID) issued by BPJS; a Secret Key for signing requests; and User Keys for specific service access (these are different per integration type). The development environment is available at api-dev.bpjsketenagakerjaan.go.id. The production environment requires a formal registration process through BPJS's e-procurement portal. Community-contributed libraries exist for Node.js (the @ssecd/jkn library on GitHub supports Node.js, Deno, and Bun) and PHP (curl-based examples on GitHub). There is no official SDK maintained by BPJS.
BPJS Kesehatan offers multiple API services: VClaim for eligibility verification and claims management (used by hospitals); PCare for primary care clinic integration (puskesmas, klinik pratama); Antrean for queue management at BPJS-registered healthcare facilities; and Apotek for pharmacy management. Authentication uses basic credentials (username/password, often encoded in base64). The new API base URL is new-api.bpjs-kesehatan.go.id/pcare-rest-v3.0 for PCare v3. Integration requires formal partnership with BPJS Kesehatan — you cannot access production APIs without a signed cooperation agreement (PKS — Perjanjian Kerja Sama).
// BPJS Ketenagakerjaan — HMAC-SHA256 Auth (Node.js)
import crypto from "crypto"
function buildBpjsHeaders(consId: string, secret: string) {
const timestamp = Math.floor(Date.now() / 1000).toString()
// Signature string: consId + "&" + timestamp
const sigString = consId + "&" + timestamp
const signature = crypto
.createHmac("sha256", secret)
.update(sigString)
.digest("base64")
return {
"X-cons-id": consId,
"X-timestamp": timestamp,
"X-signature": signature,
"Content-Type":"application/json",
}
}
// Contribution calculation (2025 rates)
function calcBpjsContributions(gaji: number) {
const capJP = 9_077_600
const capKes = 12_000_000
return {
jht_employer: gaji * 0.037,
jht_employee: gaji * 0.02,
jp_employer: Math.min(gaji, capJP) * 0.02,
jp_employee: Math.min(gaji, capJP) * 0.01,
jkk: gaji * 0.0024, // risk class 1 (office)
jkm: gaji * 0.003,
kes_employer: Math.min(gaji, capKes) * 0.04,
kes_employee: Math.min(gaji, capKes) * 0.01,
}
}
The BPJS API timestamp format is a common source of integration failures. BPJS Ketenagakerjaan's HMAC authentication requires the request timestamp in a specific format matching their server's clock within a tolerance window. If your server clock drifts by more than a few seconds, all requests will return 401 errors that look like credential problems. Always use NTP time synchronization on your integration server and log the timestamp you're sending alongside the response code when debugging BPJS authentication failures.
Before you can integrate with BPJS APIs, you need to correctly calculate contributions. The 2025 contribution rates for BPJS Ketenagakerjaan are: JKK (Jaminan Kecelakaan Kerja): 0.24%–1.74% of salary (employer, rate depends on risk class); JKM (Jaminan Kematian): 0.30% of salary (employer); JHT (Jaminan Hari Tua): 5.7% of salary total (3.7% employer + 2% employee); JP (Jaminan Pensiun): 3% of salary total (2% employer + 1% employee, capped at IDR 9,077,600 base). For BPJS Kesehatan: 5% of salary (4% employer + 1% employee, salary capped at IDR 12 million for premium calculation). These calculations are straightforward to implement but must match BPJS's own calculation exactly — discrepancies will cause reporting rejections.
The two primary integration patterns for BPJS in ERP systems are: (1) Batch monthly reporting — calculate all employee contributions for the month, generate the BPJS reporting file (usually a formatted text or Excel file that BPJS's portal accepts), and submit. This doesn't require live API integration and is sufficient for most mid-market companies. (2) Real-time eligibility checking — for HR portals where employees want to verify their BPJS status, live API calls to BPJS Ketenagakerjaan's saldo checking service return real-time JHT balance. This requires the full API integration with Cons ID and Secret Key.
# BPJS Integration Patterns
## Pattern 1: Batch Monthly Reporting (most common)
1. Calculate contributions for all employees at month-end
2. Generate BPJS reporting file (formatted text / Excel)
3. Upload via BPJS portal (no live API required)
4. Store submission receipt in ERP for audit trail
## Pattern 2: Real-time JHT Balance Check (live API)
GET https://api-dev.bpjsketenagakerjaan.go.id/telpuhrd/...
Headers: X-cons-id, X-timestamp, X-signature
Response: { "kpj": "...", "saldo": 12500000, "status": "active" }
## Pattern 3: PPOB Aggregator (no PKS required)
POST https://api.iak.id/api/v1/postpaid/pay
Body: { "product_code": "BPJSTK", "customer_id": "KPJ_NUMBER" }
# Charges per-transaction fee (~IDR 2,500–5,000)
# No direct BPJS partnership needed
Here's the authentication pattern for BPJS Ketenagakerjaan API in Node.js. The signature is generated using HMAC-SHA256 over a string composed of ConsID:timestamp:Secret. The timestamp must be in seconds since Unix epoch (not milliseconds). Request headers require X-cons-id, X-timestamp, X-signature, and the Content-Type. A critical gotcha: the BPJS staging environment sometimes returns different error codes than production — always test the exact production credentials in staging before considering integration complete.
Many Indonesian developers spend days integrating BPJS test APIs only to discover that accessing production requires a formal Perjanjian Kerja Sama (cooperation agreement) — a legal contract signed between your company and BPJS. This process takes weeks to months and requires your company to be a formal legal entity (PT or CV), have a specific use case approved by BPJS, and pass a technical review. If you're a freelancer or building a product for a client, the client's company (not yours) usually needs to be the signing party. Confirm the PKS status before committing timeline estimates to clients.
For projects where direct BPJS API integration is too complex or the PKS timeline is too long, third-party BPJS integration services exist in Indonesia. IAK API (api.iak.id) offers BPJS Ketenagakerjaan inquiry and payment as a postpaid product through their PPOB (payment point online bank) aggregator API. This is commonly used by payment apps and HR SaaS platforms to check BPJS contribution status without a direct BPJS partnership. The tradeoff: you pay a per-transaction fee to the aggregator, and you're dependent on a third party's uptime and data accuracy.
BPJS regulations and API specifications change — contribution rates have been updated multiple times in the past five years, and API endpoints have migrated. Build your BPJS integration with change in mind: put all contribution rates in a database table or config file (not hardcoded constants); implement version-aware API clients that can be updated without rewriting business logic; log all API requests and responses for debugging; and implement automated tests that validate contribution calculations against known-correct examples. When BPJS changes rates (usually announced in government regulation — Peraturan Pemerintah), you should be able to update your system in one place with confidence.