Download OpenAPI specification:
This API allows merchants to manage users, virtual accounts, deposits, and withdrawals through the CACI Clearing Gateway API.
Key Features:
API Key Authentication:
X-Api-Key headerX-Signature headerX-Timestamp headerTo access the API, your server's outbound IP address must be whitelisted by the CACI team.
401 UnauthorizedTo generate the HMAC-SHA512 signature:
"METHOD|PATH|BODY|TIMESTAMP"import hmac
import hashlib
import json
def generate_signature(method, path, raw_body, timestamp, client_secret):
"""
Generate HMAC-SHA512 signature for CACI API authentication
Args:
method: HTTP method (e.g., 'POST', 'GET')
path: API endpoint path (e.g., '/v1/transactions/fiat')
raw_body: Raw request body JSON string (empty string for GET/DELETE)
timestamp: Unix timestamp in milliseconds
client_secret: Your API secret key
Returns:
Hexadecimal signature string
"""
# Create canonical JSON with sorted keys
if raw_body:
try:
# Parse and re-sort JSON to ensure canonical ordering
body_dict = json.loads(raw_body)
canonical_body = json.dumps(body_dict, sort_keys=True, separators=(',', ':'))
except json.JSONDecodeError:
canonical_body = raw_body
else:
canonical_body = ""
# Build payload
payload = f"{method}|{path}|{canonical_body}|{timestamp}"
# Generate HMAC-SHA512 signature
signature = hmac.new(
client_secret.encode("utf-8"),
payload.encode("utf-8"),
hashlib.sha512
).hexdigest()
return signature
# Example usage
if __name__ == "__main__":
# Sample request data
method = "POST"
path = "/d/v1/users"
timestamp = 1763474667930
client_secret = "your_api_secret_here"
# Sample request body
request_data = {
"requestId": str(uuid.uuid4()),
"userId": str(uuid.uuid4())[:8],
"kycName": "Jhon Doe",
"phone": "+6281234567891",
"email": "jhon.doe@icex.id",
"type": "retail",
"nationality": "ID",
"whitelistedKycNames": [
"Jhon",
"Pg"
]
}
# Convert to canonical JSON
raw_body = json.dumps(request_data, sort_keys=True, separators=(',', ':'))
# Generate signature
signature = generate_signature(method, path, raw_body, timestamp, client_secret)
print(f"Payload: {method}|{path}|{raw_body}|{timestamp}")
print(f"Signature: {signature}")
# Headers to include in request
headers = {
"X-Api-Key": "your_api_key_here",
"X-Signature": signature,
"X-Timestamp": str(timestamp)
}
Create a new merchant user with KYC information and optional whitelisted accounts.
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key for this request |
| userId required | string Merchant user identifier |
| kycName required | string Legal/KYC name |
| phone | string User phone number with country code |
string User email | |
| type required | string Enum: "institutional" "retail" "other" User type |
| userClass | string User classification used to select FDS rule thresholds (optional). Values are defined per PAKD — each PAKD has its own set (e.g. |
| nationality | string ISO country code (2 chars) |
| idType | string Identity document type (optional) |
| idNumber | string Identity document number (optional) |
| whitelistedKycNames | Array of strings Alternate KYC names for verification |
Array of objects (WhitelistedWithdrawalAccount) Whitelisted beneficiary accounts |
{- "requestId": "2dd96e87-4f8f-460e-a04d-040370505552",
- "userId": "567890",
- "kycName": "Jhon Doe",
- "phone": "6281234567890",
- "email": "john.doe@example.com",
- "type": "retail",
- "userClass": "regular",
- "nationality": "ID",
- "idType": "ktp",
- "idNumber": "1234567890",
- "whitelistedKycNames": [
- "Jhon Doe",
- "Johnny Doe"
], - "whitelistedWithdrawalAccounts": [
- {
- "accountId": "f9f06d65-6bf1-4ded-934a-4dcf0f8f95db",
- "bankCode": "002",
- "accountNumber": "888801000003301",
- "accountHolderName": "Jhon Doe"
}
]
}{- "timestamp": 1774573934924,
- "code": 201,
- "message": "success",
- "data": {
- "userId": "567890",
- "createdAt": "2026-03-04T10:00:00Z"
}
}Retrieve detailed information of a specific merchant user.
| userId required | string Example: 1ff159d3 Merchant user identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1774573934924,
- "data": {
- "userId": "1ff159d3",
- "kycName": "Kucing",
- "phone": "+6281234567810",
- "email": "kucing@example.com",
- "type": "retail",
- "userClass": "vip1",
- "nationality": "ID",
- "idType": "ktp",
- "idNumber": "1234567810",
- "whitelistedKycNames": [
- "Kucing",
- "Liar"
], - "whitelistedWithdrawalAccounts": [
- {
- "accountHolderName": "Jhon Doe",
- "accountId": "f9f06d65-6bf1-4ded-934a-db4dcf0f8f95",
- "accountNumber": "888801000003301",
- "bankCode": "002"
}
], - "createdAt": "2026-03-26T22:13:23.971714Z",
- "updatedAt": "2026-03-26T22:13:23.971714Z"
}
}Update an existing merchant user. The userId path parameter must match the user being updated.
whitelistedWithdrawalAccounts is updated through this endpoint.
| userId required | string Example: 567890 Merchant user identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
| kycName | string |
| phone | string |
string | |
| type | string Enum: "institutional" "retail" "other" |
| userClass | string User classification used to select FDS rule thresholds (optional).
PAKD-defined values such as |
| nationality | string |
| idType | string |
| idNumber | string |
| whitelistedKycNames | Array of strings |
Array of objects (WhitelistedWithdrawalAccount) |
{- "requestId": "3eeb5958-a2f5-4934-9d8b-26197d104abb",
- "kycName": "Jhon Doe Updated",
- "phone": "6281234567890",
- "email": "john.updated@example.com",
- "type": "retail",
- "userClass": "vip1",
- "nationality": "ID",
- "idType": "ktp",
- "idNumber": "1234567890",
- "whitelistedKycNames": [
- "Jhon Doe",
- "Johnny Doe"
], - "whitelistedWithdrawalAccounts": [
- {
- "accountId": "f9f06d65-6bf1-4ded-934a-4dcf0f8f95db",
- "bankCode": "002",
- "accountNumber": "888801000003301",
- "accountHolderName": "Jhon Doe"
}
]
}{- "code": 200,
- "message": "SUCCESS",
- "reason": "User updated successfully",
- "requestId": "c4b3fa1a-9720-455d-aa23-6a0822b34193",
- "timestamp": 1774573970513
}Soft delete a merchant user. If the user has any virtual accounts, they are
automatically disabled (status set to inactive) as part of this operation.
| userId required | string Example: 567890 Merchant user identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
{- "requestId": "6b0b4af8-07e4-44fe-b4b3-32a410bbfd67"
}{- "code": 200,
- "message": "SUCCESS",
- "reason": "User deleted successfully",
- "timestamp": 1774573995972
}Retrieve all virtual accounts associated with a specific merchant user.
| userId required | string Example: USER001 Merchant user identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1774563064105,
- "data": [
- {
- "paymentInstrumentId": "019d2c0f-4746-78da-bf6e-9ab6e9bdd53f",
- "userId": "USER001",
- "bankCode": "DURIANPAY",
- "virtualAccountNo": "8808012345678901",
- "virtualAccountName": "Jhon Doe",
- "status": "active",
- "expiredDate": "2027-03-26T21:31:32.034955Z",
- "createdAt": "2026-03-26T21:31:32.034955Z",
- "updatedAt": "2026-03-26T21:31:32.034955Z"
}
]
}Returns payment instruments enabled for the authenticated merchant. Data is grouped by upstream provider so merchants can see provider-specific channel/bank availability.
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "timestamp": 1780908709325,
- "code": 200,
- "message": "SUCCESS",
- "data": [
- {
- "providerCode": "BCA",
- "providerName": "BCA Direct",
- "paymentInstruments": [
- {
- "instrumentType": "virtual_account",
- "bankCode": "014",
- "bankName": "BCA",
- "status": "active"
}
]
}, - {
- "providerCode": "DURIANPAY",
- "providerName": "DurianPay",
- "paymentInstruments": [
- {
- "instrumentType": "bank_transfer",
- "bankCode": "014",
- "bankName": "BCA",
- "status": "active"
}, - {
- "instrumentType": "bank_transfer",
- "bankCode": "008",
- "bankName": "MANDIRI",
- "status": "active"
}
]
}
]
}Create a new virtual account for a merchant user. This operation is synchronous.
Currently supported banks:
002: BRI (Bank Rakyat Indonesia)014: BCA (Bank Central Asia)503: NOBU (Bank Nationalnobu)| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
| userId required | string User to bind VA to |
| bankCode required | string Destination VA bank/channel code (BI SNAP) |
{- "requestId": "5520ef9c-e671-4c50-821d-9c9d787a5578",
- "userId": "c28f03f8",
- "bankCode": "503"
}{- "code": 200,
- "message": "SUCCESS",
- "requestId": "5520ef9c-e671-4c50-821d-9c9d787a5578",
- "timestamp": 1774723700223,
- "data": {
- "paymentInstrumentId": "019d35af-3233-7218-9135-170365519896",
- "userId": "c28f03f8",
- "bankCode": "NOBU",
- "virtualAccountNo": "662312136281234",
- "virtualAccountName": "Jhon Doe",
- "customerNo": "6281234",
- "status": "active",
- "createdAt": "2026-03-28T18:22:47.347088Z",
- "updatedAt": "2026-03-28T18:22:47.347088Z"
}
}Retrieve details of a specific virtual account by its payment instrument ID.
| paymentInstrumentId required | string <uuid> Example: 019d35af-3233-7218-9135-170365519896 Payment instrument identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1774726455840,
- "data": {
- "paymentInstrumentId": "019d35af-3233-7218-9135-170365519896",
- "merchantId": "019d2c0f-472e-755e-8c27-f2971ea622f6",
- "userId": "c28f03f8",
- "bankCode": "NOBU",
- "virtualAccountNo": "662312136281234",
- "virtualAccountName": "Jhon Doe",
- "status": "active",
- "expiresAt": "2099-12-31T23:59:59Z",
- "createdAt": "2026-03-28T18:22:47.347088Z",
- "updatedAt": "2026-03-28T18:22:47.347088Z"
}
}Update an existing virtual account. This operation is synchronous.
Expiring a virtual account: setting expiredDate to the current time or any time in the
past marks the virtual account as expired. An expired virtual account can no longer accept
payments, and a new virtual account cannot be created with the same pattern/number until
it is explicitly disabled via PUT /v1/virtual-accounts/{paymentInstrumentId}/disable.
Re-activating an expired virtual account: update expiredDate to a future date — the
same virtual account becomes usable again (its status returns to active). There is no
need to disable it first if you just want to extend/renew it.
| paymentInstrumentId required | string <uuid> Example: 019d35af-3233-7218-9135-170365519896 Payment instrument identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
| virtualAccountName | string New display name |
| expiredDate | string <date-time> New expiration timestamp. Setting this to now or a past date expires the virtual
account ( |
{- "requestId": "60a59117-5138-4cc0-ae39-babb832021ec",
- "virtualAccountName": "Jhon Doe Updated",
- "expiredDate": "2099-12-31T23:59:59Z"
}{- "code": 200,
- "message": "SUCCESS",
- "requestId": "0c2d5982-ea8c-482d-99f9-b8929fec479d",
- "timestamp": 1774755401625,
- "data": {
- "paymentInstrumentId": "019d35af-3233-7218-9135-170365519896",
- "status": "active"
}
}Disable an existing virtual account. This operation is synchronous. Once disabled, the virtual account's status becomes inactive and it can no longer accept payments.
| paymentInstrumentId required | string <uuid> Example: 01a0e721-1476-7ebe-9003-a2df29f80133 Payment instrument identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
{- "requestId": "06b38cbc-2214-1d44-b1c3-48f1dbf600da"
}{- "code": 200,
- "message": "SUCCESS",
- "requestId": "06b38cbc-2214-1d44-b1c3-48f1dbf600da",
- "timestamp": 1790584480464,
- "data": {
- "paymentInstrumentId": "01a0e721-1476-7ebe-9003-a2df29f80133",
- "status": "inactive"
}
}Fraud Detection System (FDS) checks for a proposed withdrawal.
Run the FDS check before creating a withdrawal. The check evaluates the proposed transfer against a
set of rules and returns an overall recommendation plus the individual rule results.
| Value | Meaning |
|---|---|
pass |
No rule was violated — safe to proceed with the withdrawal |
hold |
At least one rule flagged the request — route to manual review |
block |
At least one rule failed hard — do not proceed with the withdrawal |
The overall recommendation is the most severe individual result (block > hold > pass).
| Check | Scopes | What it evaluates |
|---|---|---|
amountLimit |
ip, user, member |
Accumulated withdrawal amount within a rolling time window |
velocityLimit |
ip, user |
Number of withdrawal attempts within a rolling time window |
blacklist |
— | Beneficiary account / name / IP against blacklist entries |
ipReputation |
— | Reputation of clientIp (proxy, VPN, datacenter, known-abuse) |
nameCheck |
— | Similarity between the sender (KYC) name and the beneficiary name |
Each check returns an array of results because a rule can be evaluated at multiple scopes.
fdsId so the decision can be retrieved later via GET /v1/fds/withdrawal/{fdsId}.referenceNo is your own reference for the proposed withdrawal; reuse it on the withdrawal request so the
two records can be reconciled.Run the fraud detection rules against a proposed withdrawal without creating it.
The response contains the individual rule results (data.checks) and the overall
data.recommendation (pass, hold, or block). Store data.fdsId so the decision can be
retrieved later via GET /v1/fds/withdrawal/{fdsId}.
Act on
recommendationbefore submitting the withdrawal: proceed onpass, route to manual review onhold, and reject onblock.
Rule thresholds are selected per user class. Each PAKD defines its own set of classes — for example
regular, vip1, vip2 — so userClass is a free-form string, not a fixed enum.
Resolution order for a withdrawal check:
userClass in this request body, when provideduserClass stored on the user record (set via POST /v1/users or PUT /v1/users/{userId})The class actually applied is echoed back as data.userClass.
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key for this request |
| assetId required | string Asset / currency of the proposed withdrawal |
| userId | string Merchant user identifier the withdrawal belongs to. Optional — when empty, the evaluation is attributed to a synthetic identifier derived from the merchant and beneficiary account, and user-scoped rules fall back to that identifier. Conditional: when |
| kycName | string Legal/KYC name of the user the withdrawal belongs to. Conditionally required:
|
| amount required | string Proposed withdrawal amount (decimal string) |
| beneficiaryBankCode required | string Beneficiary bank code (BI SNAP) |
| beneficiaryAccountNo required | string Beneficiary account number |
| beneficiaryAccountName required | string Beneficiary account holder name |
| clientIp | string IP address of the end user initiating the withdrawal — used by IP-scoped rules |
| referenceNo required | string Merchant reference for the proposed withdrawal |
| userClass | string User classification used to select rule thresholds (optional). PAKD-defined values such as |
{- "requestId": "ebbd0437-b456-4806-911e-e04f352bff9b",
- "assetId": "IDR",
- "userId": "567890",
- "kycName": "Jane Doe",
- "amount": "1000",
- "beneficiaryBankCode": "000",
- "beneficiaryAccountNo": "123617002261130412",
- "beneficiaryAccountName": "Jane Doe",
- "clientIp": "202.10.61.18",
- "referenceNo": "qwertyuiop123456793",
- "userClass": "regular"
}{- "timestamp": 1790050103971,
- "requestId": "ebbd0437-b456-4806-911e-e04f352bff9b",
- "code": 200,
- "message": "SUCCESS",
- "data": {
- "checks": {
- "amountLimit": [
- {
- "scope": "ip",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "distinctUsers": 1,
- "matchedWindow": "1h",
- "maxAmount": 200000000,
- "minDistinctUsers": 2
}
}, - {
- "scope": "member",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "matchedWindow": "1h",
- "maxAmount": 200000000000
}
}, - {
- "scope": "user",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "matchedWindow": "1h",
- "maxAmount": 50000000
}
}
], - "blacklist": [
- {
- "result": "pass",
- "detail": {
- "entryType": "n/a",
- "valueDisplay": "n/a"
}
}
], - "ipReputation": [
- {
- "result": "hold"
}
], - "nameCheck": [
- {
- "result": "block",
- "detail": {
- "receiverName": "Jhon Doe",
- "senderName": "Jane Doe"
}
}
], - "velocityLimit": [
- {
- "scope": "ip",
- "result": "pass",
- "detail": {
- "count": 1,
- "distinctUsers": 1,
- "matchedWindow": "1h",
- "maxCount": 5,
- "minDistinctUsers": 2
}
}, - {
- "scope": "user",
- "result": "pass",
- "detail": {
- "count": 1,
- "matchedWindow": "1h",
- "maxCount": 2
}
}
]
}, - "recommendation": "block",
- "fdsId": "76700886-1e3f-48aa-a56b-c1fdf7ed7d01",
- "assetId": "IDR",
- "userId": "fds_019d85d9-58e9-7995-869b-d2b0f4a016e8_123617002261130412",
- "amount": "1000",
- "beneficiaryBankCode": "000",
- "beneficiaryAccountNo": "123617002261130412",
- "beneficiaryAccountName": "Jane Doe",
- "clientIp": "202.10.61.18",
- "referenceNo": "qwertyuiop123456793",
- "userClass": "regular"
}
}Retrieve a previously evaluated FDS check by its fdsId. The response payload is identical to the
one returned by POST /v1/fds/withdrawal — results are immutable once recorded.
| fdsId required | string <uuid> Example: 76700886-1e3f-48aa-a56b-c1fdf7ed7d01 FDS evaluation identifier returned by the FDS check |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "timestamp": 1790050103971,
- "requestId": "ebbd0437-b456-4806-911e-e04f352bff9b",
- "code": 200,
- "message": "SUCCESS",
- "data": {
- "checks": {
- "amountLimit": [
- {
- "scope": "ip",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "distinctUsers": 1,
- "matchedWindow": "1h",
- "maxAmount": 200000000,
- "minDistinctUsers": 2
}
}, - {
- "scope": "member",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "matchedWindow": "1h",
- "maxAmount": 200000000000
}
}, - {
- "scope": "user",
- "result": "pass",
- "detail": {
- "amount": 1000,
- "matchedWindow": "1h",
- "maxAmount": 50000000
}
}
], - "blacklist": [
- {
- "result": "pass",
- "detail": {
- "entryType": "n/a",
- "valueDisplay": "n/a"
}
}
], - "ipReputation": [
- {
- "result": "hold"
}
], - "nameCheck": [
- {
- "result": "block",
- "detail": {
- "receiverName": "Jhon Doe",
- "senderName": "Jane Doe"
}
}
], - "velocityLimit": [
- {
- "scope": "ip",
- "result": "pass",
- "detail": {
- "count": 1,
- "distinctUsers": 1,
- "matchedWindow": "1h",
- "maxCount": 5,
- "minDistinctUsers": 2
}
}, - {
- "scope": "user",
- "result": "pass",
- "detail": {
- "count": 1,
- "matchedWindow": "1h",
- "maxCount": 2
}
}
]
}, - "recommendation": "block",
- "fdsId": "76700886-1e3f-48aa-a56b-c1fdf7ed7d01",
- "assetId": "IDR",
- "userId": "fds_019d85d9-58e9-7995-869b-d2b0f4a016e8_123617002261130412",
- "amount": "1000",
- "beneficiaryBankCode": "000",
- "beneficiaryAccountNo": "123617002261130412",
- "beneficiaryAccountName": "Jane Doe",
- "clientIp": "202.10.61.18",
- "referenceNo": "qwertyuiop123456793",
- "userClass": "regular"
}
}List deposit transactions with optional filters and pagination.
| status | string Enum: "completed" "failed" "pending" Filter by transaction status |
| userId | string Filter by merchant user |
| merchantWebhookStatus | string Enum: "success" "failed" Filter by webhook delivery status ( |
| startDate | string <date-time> Lower bound for requested_at (RFC3339). Max range 90 days. Defaults to 90 days before now. |
| endDate | string <date-time> Upper bound for requested_at (RFC3339). Max range 90 days. Defaults to current time. |
| trxDateTimeStart | string <date-time> Lower bound for trx_date_time (RFC3339) |
| trxDateTimeEnd | string <date-time> Upper bound for trx_date_time (RFC3339) |
| limit | integer Default: 100 Page size for pagination |
| offset | integer Default: 0 Offset for pagination |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1777025342784,
- "data": {
- "items": [
- {
- "amount": "40000.00",
- "bankReference": "d1d0fe7e-9ef0-4c9d-9b82-000000000001",
- "createdAt": "2026-04-24T08:00:00Z",
- "currency": "IDR",
- "details": {
- "beneficiaryAccountName": "Jane Doe",
- "beneficiaryAccountNo": "88082676740001",
- "beneficiaryBankCode": "014"
}, - "merchantBankFee": "0",
- "merchantFee": "0",
- "merchantFeeCalcMethod": "inclusive",
- "merchantId": "d4e5f6a7-4444-7000-dddd-000000000001",
- "merchantReference": "dep-ref-20260424-001",
- "netAmount": "40000.00",
- "paymentInstrumentId": "e5f6a7b8-5555-7000-eeee-000000000001",
- "processedAt": "2026-04-24T08:00:02Z",
- "requestedAt": "2026-04-24T08:00:00Z",
- "status": "completed",
- "merchantWebhookStatus": "success",
- "transactionFee": "0",
- "transactionId": "a1b2c3d4-1111-7000-aaaa-000000000001",
- "transactionType": "deposit",
- "updatedAt": "2026-04-24T08:00:02Z",
- "userId": "usr00002"
}, - {
- "amount": "12000.00",
- "bankReference": "e9b7803f-415f-48ea-a507-000000000002",
- "createdAt": "2026-04-24T07:30:00Z",
- "currency": "IDR",
- "details": {
- "beneficiaryAccountName": "John Smith",
- "beneficiaryAccountNo": "123617001460000001",
- "beneficiaryBankCode": "002"
}, - "merchantBankFee": "0",
- "merchantFee": "0",
- "merchantFeeCalcMethod": "inclusive",
- "merchantId": "d4e5f6a7-4444-7000-dddd-000000000001",
- "merchantReference": "dep-ref-20260424-002",
- "netAmount": "12000.00",
- "paymentInstrumentId": "e5f6a7b8-5555-7000-eeee-000000000002",
- "processedAt": "2026-04-24T07:30:02Z",
- "requestedAt": "2026-04-24T07:30:00Z",
- "status": "completed",
- "merchantWebhookStatus": "failed",
- "transactionFee": "0",
- "transactionId": "a1b2c3d4-1111-7000-aaaa-000000000002",
- "transactionType": "deposit",
- "updatedAt": "2026-04-24T07:30:02Z",
- "userId": "usr00003"
}
], - "limit": 10,
- "offset": 0,
- "totalItems": 2
}
}Retrieve details of a specific deposit transaction by its CACI transaction ID (transactionId / caciTxId).
| transactionId required | string <uuid> Example: 019d6638-6b62-7130-b90c-e4d1583f9597 Transaction identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1777025342784,
- "data": {
- "amount": "40000.00",
- "bankReference": "d1d0fe7e-9ef0-4c9d-9b82-000000000001",
- "createdAt": "2026-04-24T08:00:00Z",
- "currency": "IDR",
- "details": {
- "beneficiaryAccountName": "Jane Doe",
- "beneficiaryAccountNo": "88082676740001",
- "beneficiaryBankCode": "014"
}, - "merchantBankFee": "0",
- "merchantFee": "0",
- "merchantFeeCalcMethod": "inclusive",
- "merchantId": "d4e5f6a7-4444-7000-dddd-000000000001",
- "merchantReference": "dep-ref-20260424-001",
- "netAmount": "40000.00",
- "paymentInstrumentId": "e5f6a7b8-5555-7000-eeee-000000000001",
- "processedAt": "2026-04-24T08:00:02Z",
- "requestedAt": "2026-04-24T08:00:00Z",
- "status": "completed",
- "transactionFee": "0",
- "transactionId": "a1b2c3d4-1111-7000-aaaa-000000000001",
- "transactionType": "deposit",
- "updatedAt": "2026-04-24T08:00:02Z",
- "userId": "usr00002"
}
}Retrieve the merchant's current balances grouped by provider and vault type.
Each provider entry contains one or more balance records — typically a deposit vault
and a withdrawal vault — representing the available funds for each operation type.
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1779722046088,
- "requestId": "006b6c7b-f11d-4a6e-ab26-cadc9b53c942",
- "data": [
- {
- "providerName": "DurianPay",
- "providerType": "payment_gateway",
- "balances": [
- {
- "balance": "9999",
- "vaultType": "deposit"
}, - {
- "balance": "19995",
- "vaultType": "withdrawal"
}
]
}
]
}After a transaction is processed, the platform asynchronously POSTs a JSON payload to all webhook URLs the merchant has configured for that event type. Delivery runs in a background goroutine and never blocks the transaction response.
Transaction Processed
│
▼
Payload enriched from DB (VA info, user info)
│
▼
POST to each configured webhook URL
│
▼
WebhookLog stored in Redis
key: webhook:log:<transactionId> | TTL: 24h
| Event Type | Triggered When |
|---|---|
va_payment |
A deposit is successfully processed |
transfer |
A withdrawal status is updated |
Treat every webhook as a signal, not as proof. A webhook payload is delivered to your URL and must not be trusted on its own. After receiving a notification, your server must call the corresponding read API to confirm the transaction's authoritative status before crediting funds, releasing goods, or settling:
| Event | Confirm with | Lookup key |
|---|---|---|
va_payment (deposit) |
GET /v1/deposits/{transactionId} |
additionalInfo.caciTxId |
transfer (withdrawal) |
GET /v1/withdrawals/{transactionId} |
additionalInfo.caciTxId |
Act only on the status returned by these endpoints — not on the webhook body. This protects you against
forged or replayed notifications, since the read API is authenticated and returns CACI's source-of-truth record.
Always dedupe on caciTxId or paymentRequestId so a repeated notification never credits the same transaction twice.
POSTapplication/jsonCACI-PG-Webhook/1.0200 or 201 response from the merchant endpoint.408, 429, or any 5xx, the delivery is retried automatically — up to 2 additional attempts with a 3-second delay between each attempt. Use the retry endpoint to manually re-trigger delivery after all automatic attempts have been exhausted.Webhook requests will originate from the following IPs. Whitelist these on your server to ensure delivery:
| Environment | IP Address |
|---|---|
| Production | 16.78.217.189 |
| Staging | 15.233.128.42 |
va_payment{
"partnerServiceId": "merchant-uuid",
"customerNo": "merchant-user-id",
"virtualAccountNo": "1234567890",
"trxId": "tx-uuid",
"paymentRequestId": "tx-uuid",
"paidAmount": {
"value": "100000",
"currency": "IDR"
},
"trxDateTime": "2025-01-01T10:00:00Z",
"virtualAccountName": "John Doe",
"virtualAccountType": "01",
"virtualAccountPhone": "+62812345678",
"virtualAccountEmail": "john@example.com",
"additionalInfo": {
"minAmount": 0,
"maxAmount": 10000000,
"bankCode": "BCA",
"customerInfo": {
"customer_id": "internal-user-uuid",
"customer_ref_id": "merchant-user-ref",
"given_name": "John Doe",
"email": "john@example.com",
"mobile": "+62812345678"
},
"expiredDate": "2025-01-02T00:00:00Z",
"failureReason": null,
"merchantUserId": "merchant-user-ref",
"caciTxId": "tx-uuid"
}
}
| Field | Description |
|---|---|
partnerServiceId |
Merchant UUID (falls back to merchant ID) |
customerNo |
Merchant-side user reference |
virtualAccountNo |
VA number that received the payment |
trxId |
Internal transaction ID |
paymentRequestId |
Unique identifier for this specific payment from upstream bank or provider. |
paidAmount.value |
Amount as a string |
paidAmount.currency |
e.g. IDR |
trxDateTime |
Timestamp from the provider; falls back to processedAt |
additionalInfo.bankCode |
Bank code of the VA (e.g. BCA, BNI) |
additionalInfo.caciTxId |
Unique identifier for this specific transaction from CACI. Use this for deduplication / idempotency and to verify notifications |
additionalInfo.merchantUserId |
Merchant's user reference |
additionalInfo.customerInfo |
KYC/user details resolved from the user record |
additionalInfo.failureReason |
Populated on failed payments; null on success |
transfer{
"originalReferenceNo": "provider-reference-number",
"originalPartnerReferenceNo": "merchant-reference",
"responseCode": "00",
"responseMessage": "Success",
"amount": {
"value": "500000",
"currency": "IDR"
},
"paymentInstrumentId": "pi-uuid",
"beneficiaryAccountNo": "0987654321",
"beneficiaryBankCode": "BNI",
"sourceAccountNo": "source-account-number",
"additionalInfo": {
"merchantUserId": "merchant-user-ref",
"caciTxId": "tx-uuid"
}
}
| Field | Description |
|---|---|
originalReferenceNo |
Provider's reference number for the transfer |
originalPartnerReferenceNo |
Merchant's own reference number |
responseCode |
Provider response code |
responseMessage |
Provider response message |
amount.value |
Transfer amount as a string |
beneficiaryAccountNo |
Destination account number |
beneficiaryBankCode |
Destination bank code |
paymentInstrumentId |
Payment instrument UUID |
additionalInfo.caciTxId |
Unique identifier for this specific transaction from CACI. Use this for deduplication / idempotency and to verify notifications |
additionalInfo.merchantUserId |
Merchant's user reference |
Merchants register endpoint URLs via the endpoints below. A merchant may register multiple URLs for the same event type — the platform delivers to all of them.
Register a new merchant webhook for a specific event type.
Event types:
va_payment: Triggered on deposit (virtual account payment) eventstransfer: Triggered on withdrawal events| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key for this request |
| eventType required | string Enum: "va_payment" "transfer" "qr_payment" "debit_notify" Event type to subscribe to |
| url required | string Valid HTTPS webhook destination URL |
| headers | object Custom headers sent with every delivery |
| isActive | boolean Whether the webhook should be active. Defaults to |
| description | string Free-text label for this webhook |
{- "requestId": "2dd96e87-4f8f-460e-a04d-040370505552",
- "eventType": "va_payment",
- "headers": {
- "X-Api-Key": "your-secret"
}, - "isActive": true,
- "description": "Production deposit handler"
}{- "timestamp": 1776758146948,
- "code": 201,
- "message": "SUCCESS",
- "data": {
- "id": "754cdea6-2acd-4f11-b986-dc4276525940",
- "merchantId": "019d85d9-58e9-7995-869b-d2b0f4a016e8",
- "eventType": "va_payment",
- "headers": { },
- "isActive": true,
- "description": "",
- "createdAt": "2026-04-21T07:55:46.949308Z",
- "updatedAt": "2026-04-21T07:55:46.949308Z"
}
}Retrieve all webhooks configured for the authenticated merchant.
Event types:
va_payment: Triggered on deposit (virtual account payment) eventstransfer: Triggered on withdrawal events| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "timestamp": 1776758536186,
- "code": 200,
- "message": "SUCCESS",
- "data": [
- {
- "id": "717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514",
- "merchantId": "019d85d9-58e9-7995-869b-d2b0f4a016e8",
- "eventType": "transfer",
- "headers": { },
- "isActive": true,
- "description": "",
- "createdAt": "2026-04-21T08:01:32.080907Z",
- "updatedAt": "2026-04-21T08:01:32.080907Z"
}, - {
- "id": "754cdea6-2acd-4f11-b986-dc4276525940",
- "merchantId": "019d85d9-58e9-7995-869b-d2b0f4a016e8",
- "eventType": "va_payment",
- "headers": { },
- "isActive": true,
- "description": "",
- "createdAt": "2026-04-21T07:55:46.949308Z",
- "updatedAt": "2026-04-21T07:55:46.949308Z"
}
]
}Update an existing merchant webhook configuration.
Note:
eventTypecannot be changed after creation.
| webhookId required | string <uuid> Example: 717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514 Webhook identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
| url | string New webhook destination URL |
| headers | object Custom headers sent with every delivery |
| isActive | boolean Whether the webhook should be active |
| description | string Free-text label |
{- "requestId": "3eeb5958-a2f5-4934-9d8b-26197d104abb",
- "headers": { },
- "isActive": false,
- "description": "Disabled"
}{- "timestamp": 1776759221472,
- "code": 200,
- "message": "SUCCESS",
- "data": {
- "id": "717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514",
- "merchantId": "019d85d9-58e9-7995-869b-d2b0f4a016e8",
- "eventType": "transfer",
- "headers": { },
- "isActive": true,
- "description": "Testing. . . .",
- "createdAt": "2026-04-21T08:01:32.080907Z",
- "updatedAt": "2026-04-21T08:13:41.47397Z"
}
}Delete an existing merchant webhook.
Event types:
va_payment: Triggered on deposit (virtual account payment) eventstransfer: Triggered on withdrawal events| webhookId required | string <uuid> Example: 717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514 Webhook identifier |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
| requestId required | string Idempotency key |
{- "requestId": "6b0b4af8-07e4-44fe-b4b3-32a410bbfd67"
}{- "timestamp": 1776759320167,
- "code": 200,
- "message": "SUCCESS"
}Retrieve the delivery history for a specific transaction, showing all delivery attempts and their outcomes.
Each item represents a single delivery attempt. Multiple items with the same transactionId
indicate automatic retries — the platform retries up to 3 times (with a 3-second delay between attempts)
when the merchant endpoint responds with 408, 429, or any 5xx status code.
triggerSource values:
system — automatic delivery or retry triggered by the platformmerchant — manually triggered via the retry endpoint| transactionId required | string <uuid> Example: 019f1210-6837-7704-b699-ba207aa8d42a Transaction identifier |
| status | string Enum: "success" "failed" Filter by delivery status ( |
| limit | integer Default: 100 Page size for pagination |
| offset | integer Default: 0 Offset for pagination |
| X-Api-Key required | string Example: your-api-key Merchant API key |
| X-Signature required | string Example: a1b2c3d4e5f6... HMAC-SHA512 signature of METHOD|PATH|BODY|TIMESTAMP |
| X-Timestamp required | string Example: 1774723700223 Unix timestamp in milliseconds |
{- "code": 200,
- "message": "SUCCESS",
- "timestamp": 1782717354794,
- "data": {
- "items": [
- {
- "id": "c018dbc9-0766-43f8-abd8-c9d35efe96ea",
- "transactionId": "019f1210-6837-7704-b699-ba207aa8d42a",
- "attemptNumber": 4,
- "status": "success",
- "triggerSource": "merchant",
- "requestedAt": "2026-06-29T06:47:23.972083Z",
- "createdAt": "2026-06-29T06:47:24.248989Z"
}, - {
- "id": "decf9fea-d842-4f34-b3c1-f7bc7e0efb05",
- "transactionId": "019f1210-6837-7704-b699-ba207aa8d42a",
- "attemptNumber": 3,
- "status": "failed",
- "triggerSource": "system",
- "error": "unexpected status code: 500",
- "requestedAt": "2026-06-29T06:43:11.924086Z",
- "createdAt": "2026-06-29T06:43:18.640960Z"
}, - {
- "id": "2589362d-81d2-4908-8539-62e5e46fe9c3",
- "transactionId": "019f1210-6837-7704-b699-ba207aa8d42a",
- "attemptNumber": 2,
- "status": "failed",
- "triggerSource": "system",
- "error": "unexpected status code: 500",
- "requestedAt": "2026-06-29T06:43:11.924086Z",
- "createdAt": "2026-06-29T06:43:15.595636Z"
}, - {
- "id": "9709c02a-6186-4472-8054-66a656834665",
- "transactionId": "019f1210-6837-7704-b699-ba207aa8d42a",
- "attemptNumber": 1,
- "status": "failed",
- "triggerSource": "system",
- "error": "unexpected status code: 500",
- "requestedAt": "2026-06-29T06:43:11.924086Z",
- "createdAt": "2026-06-29T06:43:12.560146Z"
}
], - "limit": 100,
- "offset": 0,
- "totalItems": 4
}
}Retry webhook delivery for a specific transaction.
Optionally, set reportToCaci to true to also forward the notification to CACI reporting.
Duplicate reports for the same transactionId are automatically rejected — deduplication is handled server-side.
| requestId required | string Idempotency key |
| transactionId required | string Transaction identifier to retry webhook for |
| reportToCaci | boolean Default: false When |
{- "requestId": "df4d98f2-243f-4fba-8146-7102f0d21312",
- "transactionId": "019db014-af95-7294-8116-dee5285086e3",
- "reportToCaci": false
}{- "timestamp": 1782208162359,
- "requestId": "bbeff99e-223d-4294-88c6-5489cca19b45",
- "code": 200,
- "message": "SUCCESS",
- "data": {
- "transactionId": "019ea78a-51f1-7830-a268-6a8cfb78e782",
- "status": "notified",
- "attempts": 1,
- "reportToCaci": false
}
}Payment simulation endpoints for non-production environments only.
Prerequisites: Before simulating a deposit, you must complete the following steps:
POST /v1/usersPOST /v1/virtual-accountsYou can also test deposits via the UI at: https://sandbox-simulator.caci.id
Looks up a virtual account by account number and bank code, returning the VA details. Used to verify a VA exists before simulating a payment.
Non-production environments only. This endpoint is disabled in production.
| virtualAccountNo required | string Virtual account number to look up |
| bankCode required | string VA bank code — accepts BI SNAP numeric codes or provider codes |
{- "virtualAccountNo": "662312136281234",
- "bankCode": "503"
}{- "timestamp": 1774723700223,
- "code": 200,
- "message": "SUCCESS",
- "data": {
- "bankCode": "503",
- "virtualAccountNo": "662312136281234",
- "virtualAccountName": "John Doe"
}
}Simulates an inbound deposit payment to a virtual account. Creates a completed deposit transaction and triggers the configured merchant webhook.
Non-production environments only. This endpoint is disabled in production.
| virtualAccountNo required | string Virtual account number to receive the payment |
| bankCode required | string VA bank code — accepts BI SNAP numeric codes (e.g. "014" for BCA, "503" for NOBU) or provider codes (e.g. "BCA", "NOBU") |
required | object |
{- "virtualAccountNo": "662312136281234",
- "bankCode": "503",
- "payment": {
- "amount": "20000.00",
- "currency": "IDR"
}
}{- "timestamp": 1774723700223,
- "code": 201,
- "message": "SUCCESS",
- "transactionId": "2f1cf3d6-65db-447e-b68c-434efbf72479"
}kycName field to POST /v1/fds/withdrawal request body — legal/KYC name of the user the withdrawal belongs touserId and kycName on POST /v1/fds/withdrawal are conditionally required — at least one must be provided: kycName is required when userId is empty, and optional when userId is filleduserId is empty, the evaluation is attributed to a synthetic identifier derived from the merchant and beneficiary accountPUT /v1/virtual-accounts/{paymentInstrumentId}/disable endpoint to explicitly disable a virtual account (status set to inactive)Delete User (DELETE /v1/users/{userId}) behavior: any virtual accounts belonging to the user are automatically disabled as part of the deleteUpdate Virtual Account (PUT /v1/virtual-accounts/{paymentInstrumentId}) expiredDate behavior: setting it to now/a past date expires the VA (new VA with the same pattern cannot be created until it's disabled); setting it to a future date re-activates the existing VAexpired and inactive to the virtual account status enum and documented each state in VirtualAccountResponsePOST /v1/fds/withdrawal endpoint to run fraud detection rules against a proposed withdrawal without creating it — returns per-rule results and an overall recommendation (pass, hold, block)GET /v1/fds/withdrawal/{fdsId} endpoint to retrieve a previously evaluated FDS check by its fdsIdamountLimit, velocityLimit, blacklist, ipReputation, nameCheck), their scopes, and how the overall recommendation is deriveduserClass field to POST /v1/users and PUT /v1/users/{userId} request bodies, and to the Get User response / UserResponse schema — used to select FDS rule thresholdsuserClass values are defined per PAKD (e.g. regular, vip1, vip2) and accepted as a free-form string, not a fixed enumuserClass on POST /v1/fds/withdrawal is optional — resolution order is request body, then the user record, then the PAKD default thresholds; the applied value is echoed back as data.userClassGET /v1/merchant-webhooks/{transactionId}/deliveries endpoint to retrieve the delivery history for a webhook, showing all attempts and their outcomes per transaction408, 429, or any 5xx status codemerchantWebhookStatus field to API List Deposit and List Withdrawal schemas — empty by default; success when webhook was delivered, failed when all delivery attempts were unsuccessfulmerchantWebhookStatus query parameter to GET /v1/withdrawals and GET /v1/deposits — filter transactions by webhook delivery status (success or failed)reportToCaci boolean field (default false) to POST /v1/webhooks/retry request body — when true, the notification is forwarded to CACI reporting; duplicate reports for the same transactionId are automatically rejectedPOST /v1/webhooks/retry response to include reportToCaci in the data object; status field reflects notified on successGET /v1/deposits/{transactionId} endpoint to retrieve a single deposit by its CACI transaction IDpaymentRequestId and additionalInfo.caciTxId field descriptions; caciTxId is the recommended key for deduplication / idempotency and notification verificationGET /v1/balances endpoint to retrieve merchant balances per provider and vault type15.233.128.42, Staging 16.78.217.189Create Virtual Account and Update Virtual Account endpoint summaries