OpenPoly logo
Wallet Adapter

Credit

Credit contract for Opinion payouts and Polyflight wins, zero-amount loss acknowledgements, and refunds.

Credit

Credit a user wallet for an Opinion sell/redemption payout or a Polyflight win/refund. The same path also records a Polyflight losing result with an exactly zero amount and no balance change.

Path

POST /credit

Request body

{
  "external_user_id": "operator-user-123",
  "amount_minor": 15000,
  "currency_code": "IDR",
  "idempotency_key": "mini_app_redemption_item:77:credit",
  "reason": "mini_app_redemption",
  "metadata": {
    "redemptionItemId": 77,
    "marketId": 51,
    "resolveDeadlineTs": "2026-06-30T12:00:00.000Z"
  }
}

Sell payouts use the same shape with reason = "mini_app_sell_order" and metadata such as orderId, marketId, outcome, and resolveDeadlineTs.

There is no direction field in the REST v1 JSON body. The path POST /credit is the direction.

external_user_id is required here just like debit. OpenPoly gets it from the stored operator user linked to the Mini App session or redemption item.

Request fieldTypeRequirementNotes
external_user_idstringRequiredStable operator-side user ID.
amount_minorintegerRequiredPositive payout/refund amount in minor units; exactly 0 only for a valid Polyflight loss credit.
currency_codestringRequiredCurrent REST v1 value: IDR.
idempotency_keystringRequiredCredit operation key.
reasonstringRequiredOpinion reason or mini_app_polyflight_win / mini_app_polyflight_loss / mini_app_polyflight_refund.
metadataobjectRequiredAlways present, possibly empty; fields are additive.

Required credit metadata varies by flow:

FlowReasonRequired metadataOptional additive metadata
Sellmini_app_sell_orderorderId, marketIdoutcome, resolveDeadlineTs
Redemptionmini_app_redemptionredemptionItemId, marketIdresolveDeadlineTs

resolveDeadlineTs is an optional ISO-8601 string-or-null field. Current sell and redemption requests include the related market's resolution deadline when configured, otherwise null. Redemption credit does not include orderId. Do not reject unknown additive metadata fields.

Polyflight credits

EffectAmountKey
WinStored potentialPayoutMinormini_app_polyflight_ticket:{ticketId}:win
Loss acknowledgementExactly 0mini_app_polyflight_ticket:{ticketId}:loss
Cancelled-ticket refundFull original stakeMinormini_app_polyflight_ticket:{ticketId}:refund

All Polyflight credits use native IDR without an FX lookup. Wins include metadata.operation: "win" and metadata.result: "won"; losses use reason mini_app_polyflight_loss, metadata.operation: "loss", and metadata.result: "lost". Both carry the immutable ticket context and pinned transport, with no original-debit fields. A refund retains its original-debit references. See Native wallet contract for the complete zero-amount request and acknowledgement examples, not Opinion market/order fields.

Zero is valid only for that Polyflight loss contract; do not accept arbitrary zero credits. Record the losing result durably and idempotently without changing the balance. A loss obligation tracks delivery, not money owed; it causes no monetary accounting entries and emits no loss payout pending/processed callback. Historical losses are not backfilled, and already-planned/dispatched request evidence is unchanged.

Response body

Same response shape as debit:

{
  "external_user_id": "operator-user-123",
  "status": "succeeded",
  "currency_code": "IDR",
  "amount_minor": 15000,
  "idempotency_key": "mini_app_redemption_item:77:credit",
  "remote_reference": "wallet-op-456",
  "balance_minor": 115000
}

For a loss, succeeded acknowledges the recorded losing result. An optional balance_minor is the unchanged current balance and remote_reference remains optional. Timeout, pending, invalid responses, and manual resolution follow the same safety contract as monetary credits; zero does not authorize blind replay or a new key.

Status values

Response field requirements:

Response fieldTypeRequirementOpenPoly behavior
statusstringRequiredMust be succeeded, failed, or pending.
external_user_idstringOptionalFalls back to the request value. Full-contract tests reject a mismatch.
idempotency_keystringOptionalFalls back to the request key.
remote_referencestringOptionalPreserved when non-empty.
balance_minorintegerOptionalPost-operation balance, preserved for reconciliation.
failure_reasonstringOptionalRecommended when status = failed.
amount_minor, currency_codemixedOptional echoesNot consumed from the response; OpenPoly keeps the request values.
  • succeeded
  • failed
  • pending

Rules

  • payout amount already includes OpenPoly rounding rules
  • credit must be idempotent by idempotency_key
  • response should echo external_user_id when available
  • direct mutation responses must not return unknown; pending, transport failure, and invalid HTTP 2xx responses require manual review
  • without a valid contract body, HTTP 408, 429, and 5xx are ambiguous while another 4xx is failed; a valid REST v1 status body is honored even on non-2xx
  • do not blindly re-credit with a new idempotency key after timeout or manual review
  • for a Polyflight win or refund, even an explicit failed response leaves the owed obligation in manual review; it does not erase the debt
  • for a Polyflight loss, an explicit failed or ambiguous result likewise requires reconciliation of the existing zero-amount delivery; the ticket remains lost and no payout is owed
Copyright © 2026