CACI Clearing Gateway API (1.0.8)

Download OpenAPI specification:

CACI Support Team: support@caci.id License: MIT

CACI Clearing Gateway API

Merchant-facing APIs exposed by CACI Clearing Gateway for PAKD needs.

Overview

This API allows merchants to manage users, virtual accounts, deposits, and withdrawals through the CACI Clearing Gateway API.

Key Features:

  • User management (CRUD) with KYC and whitelisted accounts
  • Virtual account creation and management
  • Withdrawal processing (asynchronous)
  • Fraud detection (FDS) checks for proposed withdrawals
  • Deposit and withdrawal transaction inquiry
  • Payment instrument discovery
  • Webhooks creation and management
  • Payment simulation (non-production only)

Authentication

API Key Authentication:

  • API Key via X-Api-Key header
  • HMAC-SHA512 signature via X-Signature header
  • Unix timestamp (ms) via X-Timestamp header

IP Whitelisting

To access the API, your server's outbound IP address must be whitelisted by the CACI team.

  • Contact support@caci.id and provide your static IP address(es) to be whitelisted
  • All API requests must originate from a whitelisted IP — requests from non-whitelisted IPs will be rejected with 401 Unauthorized
  • If you have multiple environments (staging, production), provide the IPs for each separately
  • Notify the CACI team promptly if your IP address changes

Generating Signature

To generate the HMAC-SHA512 signature:

  1. Create canonical JSON: Sort the request body JSON keys alphabetically
  2. Build payload string: Format as "METHOD|PATH|BODY|TIMESTAMP"
  3. Calculate HMAC-SHA512: Use your client secret as the key
  4. Convert to hex: Lowercase hexadecimal representation

Python Implementation

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)
    }

Important Notes

  • Canonical JSON: Always sort JSON keys alphabetically
  • Timestamp: Unix timestamp in milliseconds (must be within 5 minutes of server time)
  • Empty body: For GET/DELETE requests, use empty string for BODY component
  • Path: Use only the path portion, no host or query parameters

User Management

Merchant user CRUD operations

Create User

Create a new merchant user with KYC information and optional whitelisted accounts.

Authorizations:
ApiKeyAuth
header Parameters
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

Request Body schema: application/json
required
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

email
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. regular, vip1, vip2), so this field is a free-form string rather than a fixed enum. When set, FDS uses it as the default userClass for this user's withdrawal checks; a userClass supplied on POST /v1/fds/withdrawal overrides it for that request.

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

Responses

Request samples

Content type
application/json
{
  • "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": [
    ],
  • "whitelistedWithdrawalAccounts": [
    ]
}

Response samples

Content type
application/json
{
  • "timestamp": 1774573934924,
  • "code": 201,
  • "message": "success",
  • "data": {
    }
}

Get User

Retrieve detailed information of a specific merchant user.

Authorizations:
ApiKeyAuth
path Parameters
userId
required
string
Example: 1ff159d3

Merchant user identifier

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1774573934924,
  • "data": {
    }
}

Update User

Update an existing merchant user. The userId path parameter must match the user being updated. whitelistedWithdrawalAccounts is updated through this endpoint.

Authorizations:
ApiKeyAuth
path Parameters
userId
required
string
Example: 567890

Merchant user identifier

header Parameters
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

Request Body schema: application/json
required
requestId
required
string

Idempotency key

kycName
string
phone
string
email
string
type
string
Enum: "institutional" "retail" "other"
userClass
string

User classification used to select FDS rule thresholds (optional). PAKD-defined values such as regular, vip1, vip2 — no fixed enum.

nationality
string
idType
string
idNumber
string
whitelistedKycNames
Array of strings
Array of objects (WhitelistedWithdrawalAccount)

Responses

Request samples

Content type
application/json
{
  • "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": [
    ],
  • "whitelistedWithdrawalAccounts": [
    ]
}

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "reason": "User updated successfully",
  • "requestId": "c4b3fa1a-9720-455d-aa23-6a0822b34193",
  • "timestamp": 1774573970513
}

Delete User

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.

Authorizations:
ApiKeyAuth
path Parameters
userId
required
string
Example: 567890

Merchant user identifier

header Parameters
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

Request Body schema: application/json
required
requestId
required
string

Idempotency key

Responses

Request samples

Content type
application/json
{
  • "requestId": "6b0b4af8-07e4-44fe-b4b3-32a410bbfd67"
}

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "reason": "User deleted successfully",
  • "timestamp": 1774573995972
}

List User Virtual Accounts

Retrieve all virtual accounts associated with a specific merchant user.

Authorizations:
ApiKeyAuth
path Parameters
userId
required
string
Example: USER001

Merchant user identifier

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1774563064105,
  • "data": [
    ]
}

Payment Instruments

Merchant payment instrument discovery

List Merchant Payment Instruments

Returns payment instruments enabled for the authenticated merchant. Data is grouped by upstream provider so merchants can see provider-specific channel/bank availability.

Authorizations:
ApiKeyAuth
header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "timestamp": 1780908709325,
  • "code": 200,
  • "message": "SUCCESS",
  • "data": [
    ]
}

Virtual Accounts

Virtual account creation and management

Create Virtual Account

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)
Authorizations:
ApiKeyAuth
header Parameters
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

Request Body schema: application/json
required
requestId
required
string

Idempotency key

userId
required
string

User to bind VA to

bankCode
required
string

Destination VA bank/channel code (BI SNAP)

Responses

Request samples

Content type
application/json
{
  • "requestId": "5520ef9c-e671-4c50-821d-9c9d787a5578",
  • "userId": "c28f03f8",
  • "bankCode": "503"
}

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "requestId": "5520ef9c-e671-4c50-821d-9c9d787a5578",
  • "timestamp": 1774723700223,
  • "data": {
    }
}

Get Virtual Account

Retrieve details of a specific virtual account by its payment instrument ID.

Authorizations:
ApiKeyAuth
path Parameters
paymentInstrumentId
required
string <uuid>
Example: 019d35af-3233-7218-9135-170365519896

Payment instrument identifier

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1774726455840,
  • "data": {
    }
}

Update Virtual Account

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.

Authorizations:
ApiKeyAuth
path Parameters
paymentInstrumentId
required
string <uuid>
Example: 019d35af-3233-7218-9135-170365519896

Payment instrument identifier

header Parameters
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

Request Body schema: application/json
required
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 (status becomes expired); setting it to a future date re-activates it (status becomes active).

Responses

Request samples

Content type
application/json
Example
{
  • "requestId": "60a59117-5138-4cc0-ae39-babb832021ec",
  • "virtualAccountName": "Jhon Doe Updated",
  • "expiredDate": "2099-12-31T23:59:59Z"
}

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "requestId": "0c2d5982-ea8c-482d-99f9-b8929fec479d",
  • "timestamp": 1774755401625,
  • "data": {
    }
}

Disable Virtual Account

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.

Authorizations:
ApiKeyAuth
path Parameters
paymentInstrumentId
required
string <uuid>
Example: 01a0e721-1476-7ebe-9003-a2df29f80133

Payment instrument identifier

header Parameters
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

Request Body schema: application/json
required
requestId
required
string

Idempotency key

Responses

Request samples

Content type
application/json
{
  • "requestId": "06b38cbc-2214-1d44-b1c3-48f1dbf600da"
}

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "requestId": "06b38cbc-2214-1d44-b1c3-48f1dbf600da",
  • "timestamp": 1790584480464,
  • "data": {
    }
}

Withdrawals

Withdrawal processing and inquiry

FDS Check

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.

Recommendations

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).

Rule Groups

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.

Usage Notes

  • Running a check does not create a withdrawal — it is a read-only evaluation that records a result.
  • Persist the returned 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 recommendation before submitting the withdrawal: proceed on pass, route to manual review on hold, and reject on block.

User Class

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:

  1. userClass in this request body, when provided
  2. userClass stored on the user record (set via POST /v1/users or PUT /v1/users/{userId})
  3. The PAKD's default thresholds, when neither is set

The class actually applied is echoed back as data.userClass.

Authorizations:
ApiKeyAuth
header Parameters
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

Request Body schema: application/json
required
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 userId is empty, kycName becomes required.

kycName
string

Legal/KYC name of the user the withdrawal belongs to.

Conditionally required:

  • Required when userId is empty or not provided
  • Optional when userId is filled
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 regular, vip1, vip2 — no fixed enum. When omitted, the userClass stored on the user record (set via POST /v1/users or PUT /v1/users/{userId}) is used.

Responses

Request samples

Content type
application/json
{
  • "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"
}

Response samples

Content type
application/json
{
  • "timestamp": 1790050103971,
  • "requestId": "ebbd0437-b456-4806-911e-e04f352bff9b",
  • "code": 200,
  • "message": "SUCCESS",
  • "data": {
    }
}

Get FDS Result

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.

Authorizations:
ApiKeyAuth
path Parameters
fdsId
required
string <uuid>
Example: 76700886-1e3f-48aa-a56b-c1fdf7ed7d01

FDS evaluation identifier returned by the FDS check

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "timestamp": 1790050103971,
  • "requestId": "ebbd0437-b456-4806-911e-e04f352bff9b",
  • "code": 200,
  • "message": "SUCCESS",
  • "data": {
    }
}

Deposits

Deposit transaction inquiry

List Deposits

List deposit transactions with optional filters and pagination.

Authorizations:
ApiKeyAuth
query Parameters
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 (success or failed)

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

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1777025342784,
  • "data": {
    }
}

Get Deposit

Retrieve details of a specific deposit transaction by its CACI transaction ID (transactionId / caciTxId).

Authorizations:
ApiKeyAuth
path Parameters
transactionId
required
string <uuid>
Example: 019d6638-6b62-7130-b90c-e4d1583f9597

Transaction identifier

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1777025342784,
  • "data": {
    }
}

Balances

Merchant balance inquiry per provider and vault type

Get Balances

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.

Authorizations:
ApiKeyAuth
header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1779722046088,
  • "requestId": "006b6c7b-f11d-4a6e-ab26-cadc9b53c942",
  • "data": [
    ]
}

Webhooks

Merchant webhook configuration, retry operations, and outgoing notification reference.


Outgoing Notifications

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.

Flow

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 Types

Event Type Triggered When
va_payment A deposit is successfully processed
transfer A withdrawal status is updated

Verifying Notifications (Required)

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.

Delivery Details

  • Method: POST
  • Content-Type: application/json
  • User-Agent: CACI-PG-Webhook/1.0
  • Custom headers defined per webhook config are merged into every request.
  • Success is determined by an HTTP 200 or 201 response from the merchant endpoint.
  • The platform makes up to 3 delivery attempts per notification. If the merchant endpoint returns an error with status code 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.

Source IP Addresses

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

Payload Schemas

Deposit — 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

Withdrawal — 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

Configuration

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.

Create Webhook

Register a new merchant webhook for a specific event type.

Event types:

  • va_payment: Triggered on deposit (virtual account payment) events
  • transfer: Triggered on withdrawal events
Authorizations:
ApiKeyAuth
header Parameters
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

Request Body schema: application/json
required
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 true.

description
string

Free-text label for this webhook

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "timestamp": 1776758146948,
  • "code": 201,
  • "message": "SUCCESS",
  • "data": {
    }
}

List Webhooks

Retrieve all webhooks configured for the authenticated merchant.

Event types:

  • va_payment: Triggered on deposit (virtual account payment) events
  • transfer: Triggered on withdrawal events
Authorizations:
ApiKeyAuth
header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "timestamp": 1776758536186,
  • "code": 200,
  • "message": "SUCCESS",
  • "data": [
    ]
}

Update Webhook

Update an existing merchant webhook configuration.

Note: eventType cannot be changed after creation.

Authorizations:
ApiKeyAuth
path Parameters
webhookId
required
string <uuid>
Example: 717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514

Webhook identifier

header Parameters
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

Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "timestamp": 1776759221472,
  • "code": 200,
  • "message": "SUCCESS",
  • "data": {
    }
}

Delete Webhook

Delete an existing merchant webhook.

Event types:

  • va_payment: Triggered on deposit (virtual account payment) events
  • transfer: Triggered on withdrawal events
Authorizations:
ApiKeyAuth
path Parameters
webhookId
required
string <uuid>
Example: 717bf9b3-e8f2-4bf7-82fa-00a6bdaa7514

Webhook identifier

header Parameters
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

Request Body schema: application/json
required
requestId
required
string

Idempotency key

Responses

Request samples

Content type
application/json
{
  • "requestId": "6b0b4af8-07e4-44fe-b4b3-32a410bbfd67"
}

Response samples

Content type
application/json
{
  • "timestamp": 1776759320167,
  • "code": 200,
  • "message": "SUCCESS"
}

List Webhook Deliveries

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 platform
  • merchant — manually triggered via the retry endpoint
Authorizations:
ApiKeyAuth
path Parameters
transactionId
required
string <uuid>
Example: 019f1210-6837-7704-b699-ba207aa8d42a

Transaction identifier

query Parameters
status
string
Enum: "success" "failed"

Filter by delivery status (success or failed)

limit
integer
Default: 100

Page size for pagination

offset
integer
Default: 0

Offset for pagination

header Parameters
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

Responses

Response samples

Content type
application/json
{
  • "code": 200,
  • "message": "SUCCESS",
  • "timestamp": 1782717354794,
  • "data": {
    }
}

Webhook Retry

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.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
requestId
required
string

Idempotency key

transactionId
required
string

Transaction identifier to retry webhook for

reportToCaci
boolean
Default: false

When true, the notification is also forwarded to CACI reporting. Duplicate reports for the same transactionId are rejected automatically — no action needed on your side.

Responses

Request samples

Content type
application/json
{
  • "requestId": "df4d98f2-243f-4fba-8146-7102f0d21312",
  • "transactionId": "019db014-af95-7294-8116-dee5285086e3",
  • "reportToCaci": false
}

Response samples

Content type
application/json
{
  • "timestamp": 1782208162359,
  • "requestId": "bbeff99e-223d-4294-88c6-5489cca19b45",
  • "code": 200,
  • "message": "SUCCESS",
  • "data": {
    }
}

Payment Simulator

Payment simulation endpoints for non-production environments only.

Prerequisites: Before simulating a deposit, you must complete the following steps:

  1. Create a user via POST /v1/users
  2. Create a virtual account for that user via POST /v1/virtual-accounts
  3. Use the simulator to send a test payment to the created VA

You can also test deposits via the UI at: https://sandbox-simulator.caci.id

Simulate Deposit Inquiry

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.

Request Body schema: application/json
required
virtualAccountNo
required
string

Virtual account number to look up

bankCode
required
string

VA bank code — accepts BI SNAP numeric codes or provider codes

Responses

Request samples

Content type
application/json
{
  • "virtualAccountNo": "662312136281234",
  • "bankCode": "503"
}

Response samples

Content type
application/json
{
  • "timestamp": 1774723700223,
  • "code": 200,
  • "message": "SUCCESS",
  • "data": {
    }
}

Simulate Deposit Payment

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.

Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{
  • "virtualAccountNo": "662312136281234",
  • "bankCode": "503",
  • "payment": {
    }
}

Response samples

Content type
application/json
{
  • "timestamp": 1774723700223,
  • "code": 201,
  • "message": "SUCCESS",
  • "transactionId": "2f1cf3d6-65db-447e-b68c-434efbf72479"
}

Changelog

v1.0.8 — 2026-10-19

  • Added kycName field to POST /v1/fds/withdrawal request body — legal/KYC name of the user the withdrawal belongs to
  • userId 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 filled
  • Clarified that when userId is empty, the evaluation is attributed to a synthetic identifier derived from the merchant and beneficiary account

v1.0.7 — 2026-10-05

  • Added PUT /v1/virtual-accounts/{paymentInstrumentId}/disable endpoint to explicitly disable a virtual account (status set to inactive)
  • Clarified Delete User (DELETE /v1/users/{userId}) behavior: any virtual accounts belonging to the user are automatically disabled as part of the delete
  • Clarified Update 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 VA
  • Added expired and inactive to the virtual account status enum and documented each state in VirtualAccountResponse

v1.0.6 — 2026-09-22

  • Added POST /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)
  • Added GET /v1/fds/withdrawal/{fdsId} endpoint to retrieve a previously evaluated FDS check by its fdsId
  • Documented the FDS rule groups (amountLimit, velocityLimit, blacklist, ipReputation, nameCheck), their scopes, and how the overall recommendation is derived
  • Added optional userClass 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 thresholds
  • userClass values are defined per PAKD (e.g. regular, vip1, vip2) and accepted as a free-form string, not a fixed enum
  • userClass 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.userClass
  • Added Fraud detection (FDS) checks for proposed withdrawals to the Key Features list in the overview

v1.0.5 — 2026-06-29

  • Added GET /v1/merchant-webhooks/{transactionId}/deliveries endpoint to retrieve the delivery history for a webhook, showing all attempts and their outcomes per transaction
  • Updated webhook delivery behavior: the platform now automatically retries delivery up to 3 times (2 additional attempts after the first) with a 3-second delay between attempts — retries are triggered when the merchant endpoint returns 408, 429, or any 5xx status code
  • Added merchantWebhookStatus field to API List Deposit and List Withdrawal schemas — empty by default; success when webhook was delivered, failed when all delivery attempts were unsuccessful
  • Added merchantWebhookStatus query parameter to GET /v1/withdrawals and GET /v1/deposits — filter transactions by webhook delivery status (success or failed)

v1.0.4 — 2026-06-09

  • Added 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 rejected
  • Updated POST /v1/webhooks/retry response to include reportToCaci in the data object; status field reflects notified on success

v1.0.3 — 2026-06-02

  • Added GET /v1/deposits/{transactionId} endpoint to retrieve a single deposit by its CACI transaction ID
  • Added Verifying Notifications (Required) guidance in Webhooks — confirm each notification via the read API and deduplicate so a repeated notification is never processed twice (idempotency)
  • Clarified paymentRequestId and additionalInfo.caciTxId field descriptions; caciTxId is the recommended key for deduplication / idempotency and notification verification

v1.0.2 — 2026-05-25

  • Added GET /v1/balances endpoint to retrieve merchant balances per provider and vault type
  • Added IP Whitelisting section in the overview with instructions to contact support
  • Added Source IP Addresses table in Webhooks — Production 15.233.128.42, Staging 16.78.217.189

v1.0.1 — 2026-05-20

  • Updated Virtual Account creation and update operations from asynchronous to synchronous
  • Removed "(Async)" suffix from Create Virtual Account and Update Virtual Account endpoint summaries
  • Clarified Key Features description: virtual account management no longer labeled as asynchronous

v1.0.0 — Initial Release

  • User management (CRUD) with KYC and whitelisted accounts
  • Virtual account creation and management
  • Withdrawal processing (asynchronous)
  • Deposit and withdrawal transaction inquiry
  • Payment instrument discovery
  • Webhooks creation and management
  • Payment simulation (non-production only)