Simulator
Simulator
Simulator endpoints are staging-only helpers for onboarding and demo flows.
Availability
- onboarding and staging only
- not production integration path
- regular staging integration should still use the operator's real
balance_adapter_type(typicallyrest_v1) - simulator endpoints operate on simulator-only operator users (
metadata.simulator = true) and must not affect the operator's real integration mode
Paths
GET /api/operator/simulator/usersPOST /api/operator/simulator/usersGET /api/operator/simulator/users/{id}PATCH /api/operator/simulator/users/{id}POST /api/operator/simulator/users/{id}/operationsPOST /api/operator/simulator/operationsGET /api/operator/simulator/wallets/{externalUserId}PUT /api/operator/simulator/wallets/{externalUserId}
Required scopes:
simulator:readfor list, detail, and wallet readssimulator:writefor create/update/wallet/operation mutation
Path and query parameters
| Endpoint | Parameter | Requirement | Notes |
|---|---|---|---|
GET /simulator/users | status | Optional | active, suspended, or disabled. |
GET /simulator/users | search | Optional | Matches external user ID, display name, or email. |
GET /simulator/users | sort | Optional | updated_at_desc (default), created_at_desc, or external_user_id_asc. |
GET /simulator/users | page, page_size | Optional | Defaults 1 and 25; page_size maximum 100. |
/simulator/users/{id} routes | id path parameter | Required | Positive simulator operator-user ID. |
/simulator/wallets/{externalUserId} routes | externalUserId path parameter | Required | URL-encoded external user ID. |
Simulator user create body
{
"external_user_id": "operator-user-123",
"display_name": "Demo User",
"email": "demo@example.com",
"locale": "id",
"balance_minor": 500000,
"metadata": {}
}
| Field | Type | Requirement | Values/default |
|---|---|---|---|
external_user_id | string | Required | Non-empty operator-side user ID. |
balance_minor | non-negative integer | Required | Initial wallet balance in operator currency minor units; 0 is valid. |
display_name | string | Optional | Defaults to null. |
email | string | Optional | Stored in simulator metadata; no email-format validation is applied by this endpoint. |
locale | string | Optional | Defaults to the operator default locale, then en. |
metadata | object | Optional | Defaults to {}. The API always forces simulator: true and the selected locale. |
The API adds metadata.simulator = true and stores simulator wallet balance in operator currency.
Simulator user update body
PATCH /api/operator/simulator/users/{id} accepts display_name, email, status, locale, and metadata.
| Field | Type | Requirement | Notes |
|---|---|---|---|
display_name | string | Optional | Current behavior: omission or an empty value clears the display name. |
email | string | Optional | Current behavior: omission or an empty value clears the stored email. |
status | string | Optional | active, suspended, or disabled; omission keeps the current status. |
locale | string | Optional | Omission keeps the current/operator default locale. |
metadata | object | Optional | Omission preserves current metadata. simulator: true and locale are forced. |
The patch endpoint does not require at least one field. Because omitted display_name and email currently clear those values, send both explicitly when they must be preserved.
Operation create body
For POST /api/operator/simulator/operations, all four fields below are required:
{
"external_user_id": "operator-user-123",
"direction": "debit",
"amount_minor": 10000,
"idempotency_key": "demo-op-123"
}
| Field | Type | Requirement | Notes |
|---|---|---|---|
external_user_id | string | Required | Must identify a simulator-tagged user. |
direction | string | Required | One of the allowed directions below. |
amount_minor | positive integer | Required | Operator currency minor units. |
idempotency_key | string | Required | Non-empty and unique for the intended operation. |
For POST /api/operator/simulator/users/{id}/operations, direction and amount_minor are required. idempotency_key is optional; when omitted, the API generates one. The user is selected by the required {id} path parameter, so external_user_id is not sent in that body.
Simulator wallet body
PUT /api/operator/simulator/wallets/{externalUserId} creates or updates a simulator user and wallet.
| Field | Type | Requirement | Values/default |
|---|---|---|---|
balance_minor | non-negative integer | Required | Replaces the wallet balance; 0 is valid. |
display_name | string | Optional | Display-name snapshot for an upserted user. |
currency_code | string | Optional | Defaults to the operator currency. |
GET /api/operator/simulator/wallets/{externalUserId} has no query parameters or body. A missing simulator user/wallet returns 200 with exists: false and balance_minor: 0; it is not a 404.
Operation responses
Operation create responses include external_user_id when the operation is tied to a simulator user:
{
"id": 123,
"external_user_id": "operator-user-123",
"status": "succeeded",
"direction": "debit",
"amount_minor": 10000,
"currency_code": "IDR",
"idempotency_key": "demo-op-123"
}
Allowed directions
debitcreditdebit_reversalcredit_reversal
Failure cases
- simulator not enabled ->
404 SIMULATOR_DISABLED - unknown operator user ->
404 OPERATOR_USER_NOT_FOUND - unknown simulator user ID ->
404 SIMULATOR_USER_NOT_FOUND - invalid direction ->
400 INVALID_DIRECTION
