Debit
Debit
Debit a user wallet for an Opinion buy or Polyflight ticket purchase.
Path
POST /debit
Request body
{
"external_user_id": "operator-user-123",
"amount_minor": 10000,
"currency_code": "IDR",
"idempotency_key": "mini_app_order:1:debit",
"reason": "mini_app_buy_order",
"metadata": {
"orderId": 1,
"marketId": 51,
"outcome": "yes",
"resolveDeadlineTs": "2026-06-30T12:00:00.000Z"
}
}
There is no direction field in the REST v1 JSON body. The path POST /debit is the direction.
| Request field | Type | Requirement | Notes |
|---|---|---|---|
external_user_id | string | Required | Stable operator-side user ID. |
amount_minor | integer | Required | Exact debit amount in minor units. |
currency_code | string | Required | Current REST v1 value: IDR. |
idempotency_key | string | Required | Debit operation key. |
reason | string | Required | mini_app_buy_order for Opinion or mini_app_polyflight_purchase for Polyflight. |
metadata | object | Required | Always present, possibly empty; fields are additive. |
Required buy-debit metadata:
| Metadata field | Type | Requirement |
|---|---|---|
orderId | positive integer | Required |
marketId | positive integer | Required |
resolveDeadlineTs | ISO-8601 string or null | Optional |
Current buy requests also include outcome and resolveDeadlineTs as additive context. resolveDeadlineTs is the related market's resolution deadline when configured, otherwise null. Do not reject unknown metadata fields.
Polyflight purchase debit
Polyflight sends the same top-level body with reason = "mini_app_polyflight_purchase", amount equal to the native IDR ticket stake, and key mini_app_polyflight_purchase:{purchaseRequestId}:debit. Its metadata contains product, operation, purchase/flight/choice identifiers, and the immutable native quote and flight times. It does not include an Opinion marketId, orderId, or FX snapshot. See Native wallet contract.
Response body
{
"external_user_id": "operator-user-123",
"status": "succeeded",
"currency_code": "IDR",
"amount_minor": 10000,
"idempotency_key": "mini_app_order:1:debit",
"remote_reference": "wallet-op-123",
"balance_minor": 90000
}
Status values
Response field requirements:
| Response field | Type | Requirement | OpenPoly behavior |
|---|---|---|---|
status | string | Required | Must be succeeded, failed, or pending. Any other/missing value is an invalid response. |
external_user_id | string | Optional | Falls back to the request value. Full-contract tests reject a mismatch. |
idempotency_key | string | Optional | Falls back to the request key. |
remote_reference | string | Optional | Preserved when non-empty. |
balance_minor | integer | Optional | Post-operation balance, preserved for reconciliation. |
failure_reason | string | Optional | Recommended when status = failed. |
amount_minor, currency_code | mixed | Optional echoes | Not consumed from the response; OpenPoly keeps the request values. |
succeededfailedpending
Direct mutation responses must not return unknown. A pending response is treated as ambiguous and sent to manual review; OpenPoly does not automatically replay the primary debit.
Rules
- same idempotency key must return same effective result
- response should echo
external_user_idwhen available - do not silently convert failure into success
- do not debit the same user twice for the same
idempotency_key - timeout, network failure,
pending, HTTP408, HTTP429, and HTTP5xxare ambiguous; investigate the original idempotency key manually
