Baca bagian ini sekali sebelum memakai modul apa pun: format response envelope, pola soft-error (HTTP 200 tapi gagal di sisi DJP), kode status HTTP, format tanggal dan angka, idempotensi, rate limit, caching, dan penanganan field sensitif.
This section defines patterns used across every Sipajak API endpoint. Read it once; subsequent module sections assume familiarity with these conventions.
3.1 Response envelope
Every JSON response uses the same outer envelope:
{
"status_code": 200,
"message": "OK",
"data": { ... }
}| Field | Type | Description |
|---|---|---|
status_code | integer | Always 200 on a successful HTTP exchange. Mirrors the HTTP status loosely but is not always identical — trust the HTTP status code over this field for transport-level outcomes. |
message | string | Human-readable transport status, typically "OK". |
data | object | Endpoint-specific payload. Shape varies per endpoint. |
3.1.1 Two shapes of data
The shape of data depends on whether the endpoint proxies an upstream call to DJP or simply enqueues asynchronous work.
A. DJP-passthrough endpoints
Endpoints that synchronously call DJP (e.g. POST /v2/vswp) return a data object that surfaces the DJP response inside the envelope:
{
"status_code": 200,
"message": "OK",
"data": {
"status": "1",
"statusMessage": "Success",
"result": { ... },
"uuid": "..."
}
}B. Async-job endpoints
Endpoints that enqueue work (e.g. POST /v1/vswp/bulking) return a data object containing only a job identifier, with no DJP-shaped fields:
{
"status_code": 200,
"message": "OK",
"data": {
"job_id": "..."
}
}Each endpoint reference in this document states which shape it returns.
3.2 The soft-error pattern (important)
Important. Sipajak proxies DJP for many operations. DJP business-level errors (taxpayer not found, certificate expired, period closed, etc.) are returned inside the response envelope rather than via HTTP error codes. A DJP-passthrough endpoint can return HTTP 200 while indicating a business failure.
For DJP-passthrough endpoints, integrators must inspect data.status:
| data.status | Meaning |
|---|---|
"1" | Business success. data.result is present. |
"0" | Business failure. data.statusMessage carries the upstream error message (often in Bahasa Indonesia, e.g. "Wajib Pajak tidak ditemukan"). data.result is absent. |
Recommended client-side handling:
const res = await fetch(...);
if (!res.ok) {
// transport error (auth, network, 5xx) — handle separately
}
const body = await res.json();
if (body.data.status !== "1") {
// business failure — body.data.statusMessage explains why
}HTTP error codes (4xx, 5xx) signal transport-level failures only — authentication, authorisation, malformed JSON, timeouts, gateway errors. Business outcomes always travel in the envelope.
3.3 HTTP status codes
| Code | Meaning |
|---|---|
200 | Request was processed at the transport layer. Inspect data.status for business outcome. |
400 | Malformed request (invalid JSON, missing required body field for some endpoints). |
401 | Missing or invalid Basic Auth credentials. |
403 | Credential valid but NPWP in header not registered for this client. |
413 | Multipart upload exceeds the size limit. |
500 | Server error. Safe to retry with exponential backoff. |
502 / 504 | Upstream DJP error or timeout. Safe to retry. |
Note: not every endpoint validates input format strictly. Where validation is loose, malformed input is forwarded to DJP and returns through the soft-error envelope rather than an HTTP 400. Each endpoint reference documents its specific validation behaviour.
3.4 Field naming
Request and response bodies use camelCase for tax-domain fields (e.g. npwpPenjual, tahunPajak, kodeObjekPajak) and snake_case for transport/envelope fields (status_code, job_id, job_vswp_id). This split reflects different origin systems — domain fields mirror DJP's data model; envelope fields are Sipajak's own.
3.5 Date and time formats
Note. The API is inconsistent about date formats across modules. Each endpoint reference documents the specific format it expects. Do not assume a single format applies across the API.
| Format | Where it appears |
|---|---|
DDMMYYYY (no separator) | Some /v2/efaktur/* request bodies (e.g. tanggalFaktur) |
YYYY-MM-DD HH:mm:ss | /v2/ebilling/* request bodies (requestDate) |
ISO 8601 UTC (YYYY-MM-DDTHH:mm:ss.sssZ) | All response timestamps; created_time fields |
MM (string, two-digit) | masaPajak (tax period) |
YYYY (string, four-digit; sometimes integer) | tahunPajak (tax year) |
3.6 Numeric formats
Currency and tax amounts are passed as JSON numbers without thousands separators. Decimal tax rates use fractional form (0.11 for 11% PPN), not percentage form (11).
{
"hargaSatuan": 100000,
"dpp": 1000000,
"tarifPpn": 0.11,
"ppn": 110000
}3.7 Pagination
List endpoints accept limit and page query parameters (1-indexed). Defaults vary per endpoint; check the endpoint reference. Cursor-based pagination is not currently supported.
3.8 Idempotency
There is no Idempotency-Key header today. Endpoints that create domain records (e.g. create-faktur-pk, create-bpu-a0-21) are idempotent by natural key — re-submitting the same identifying tuple (e.g. nomorFaktur + tahunPajak) will be rejected by DJP with a business-level error rather than creating a duplicate. Integrators that need at-least-once delivery semantics should treat a duplicate-key business error as success on retry.
Asynchronous job submissions (such as /v1/vswp/bulking) are not idempotent — re-submitting the same file produces a new job_id.
3.9 Rate limits
Rate limits are not currently published. Sustained bursts above a few hundred requests per minute per NPWP may be throttled at the gateway. If you anticipate consistent high volume, contact Sipajak before going live.
3.10 Caching
POST /v2/vswp results are cached server-side with a 90-day TTL keyed on (npwp, tujuan). Repeat lookups for the same pair within the TTL return in roughly 10ms without consulting DJP. There is no cache-bypass header.
No other endpoint advertises cache behaviour.
3.11 Sensitive fields
Some endpoints accept secret material directly in the request body — notably passphrasePenandatangan (certificate passphrase) on e-Faktur and e-Bupot create endpoints. These bodies must travel over TLS (the API does not accept plaintext HTTP) and must not be logged client-side. Treat any field whose name contains passphrase, password, secret, token, or key as sensitive.