OpenPoly logo
Wallet Adapter

Debit

Debit contract for buy-side wallet mutations.

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 fieldTypeRequirementNotes
external_user_idstringRequiredStable operator-side user ID.
amount_minorintegerRequiredExact debit amount in minor units.
currency_codestringRequiredCurrent REST v1 value: IDR.
idempotency_keystringRequiredDebit operation key.
reasonstringRequiredmini_app_buy_order for Opinion or mini_app_polyflight_purchase for Polyflight.
metadataobjectRequiredAlways present, possibly empty; fields are additive.

Required buy-debit metadata:

Metadata fieldTypeRequirement
orderIdpositive integerRequired
marketIdpositive integerRequired
resolveDeadlineTsISO-8601 string or nullOptional

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 fieldTypeRequirementOpenPoly behavior
statusstringRequiredMust be succeeded, failed, or pending. Any other/missing value is an invalid response.
external_user_idstringOptionalOmitted/null falls back to the request value; a supplied value must match the requested user after trimming.
idempotency_keystringOptionalOmitted/null falls back to the request key; a supplied value must match exactly.
remote_referencestringOptionalPreserved when non-empty; maximum 255 characters.
balance_minorintegerOptionalPost-operation balance, preserved for reconciliation.
failure_reasonstringOptionalRecommended when status = failed.
amount_minor, currency_codeinteger, stringOptional echoesOmitted/null uses request values. A supplied amount must equal the requested safe integer; currency must match exactly.
  • succeeded
  • failed
  • pending

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.

A conflicting identity echo produces REST_V1_MUTATION_IDENTITY_MISMATCH and an internal unknown outcome, including on a non-2xx status body. A mismatched receipt is not proof that the intended debit failed; reconcile the original key before further action.

Rules

  • same idempotency key must return same effective result
  • response should echo external_user_id when available
  • do not silently convert failure into success
  • do not debit the same user twice for the same idempotency_key
  • timeout, network failure, or an invalid HTTP 2xx response is ambiguous; without a valid contract body, HTTP 408, 429, and 5xx are ambiguous while another 4xx is failed
Copyright © 2026