OpenPoly logo
Operator Api

Simulator

Staging-only simulator endpoints for operator onboarding and drills.

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 (typically rest_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/users
  • POST /api/operator/simulator/users
  • GET /api/operator/simulator/users/{id}
  • PATCH /api/operator/simulator/users/{id}
  • POST /api/operator/simulator/users/{id}/operations
  • POST /api/operator/simulator/operations
  • GET /api/operator/simulator/wallets/{externalUserId}
  • PUT /api/operator/simulator/wallets/{externalUserId}

Required scopes:

  • simulator:read for list, detail, and wallet reads
  • simulator:write for create/update/wallet/operation mutation

Path and query parameters

EndpointParameterRequirementNotes
GET /simulator/usersstatusOptionalactive, suspended, or disabled.
GET /simulator/userssearchOptionalMatches external user ID, display name, or email.
GET /simulator/userssortOptionalupdated_at_desc (default), created_at_desc, or external_user_id_asc.
GET /simulator/userspage, page_sizeOptionalDefaults 1 and 25; page_size maximum 100.
/simulator/users/{id} routesid path parameterRequiredPositive simulator operator-user ID.
/simulator/wallets/{externalUserId} routesexternalUserId path parameterRequiredURL-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": {}
}
FieldTypeRequirementValues/default
external_user_idstringRequiredNon-empty operator-side user ID.
balance_minornon-negative integerRequiredInitial wallet balance in operator currency minor units; 0 is valid.
display_namestringOptionalDefaults to null.
emailstringOptionalStored in simulator metadata; no email-format validation is applied by this endpoint.
localestringOptionalDefaults to the operator default locale, then en.
metadataobjectOptionalDefaults 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.

FieldTypeRequirementNotes
display_namestringOptionalCurrent behavior: omission or an empty value clears the display name.
emailstringOptionalCurrent behavior: omission or an empty value clears the stored email.
statusstringOptionalactive, suspended, or disabled; omission keeps the current status.
localestringOptionalOmission keeps the current/operator default locale.
metadataobjectOptionalOmission 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"
}
FieldTypeRequirementNotes
external_user_idstringRequiredMust identify a simulator-tagged user.
directionstringRequiredOne of the allowed directions below.
amount_minorpositive integerRequiredOperator currency minor units.
idempotency_keystringRequiredNon-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.

FieldTypeRequirementValues/default
balance_minornon-negative integerRequiredReplaces the wallet balance; 0 is valid.
display_namestringOptionalDisplay-name snapshot for an upserted user.
currency_codestringOptionalDefaults 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

  • debit
  • credit
  • debit_reversal
  • credit_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
Copyright © 2026