OpenPoly logo
Wallet Adapter

Overview

Overview of the operator wallet adapter contract.

Overview

Wallet adapter is operator-owned server-to-server API used by OpenPoly to read balances and execute wallet mutations.

Ownership split

OpenPoly owns:

  • Opinion quote, order, and redemption pipeline
  • Polyflight native-IDR ticket and payment state
  • signed webhook dispatch Operator owns:
  • wallet balances
  • debit and credit execution
  • wallet auth secret
  • wallet operation observability

Required contract paths

  • GET /balance
  • POST /debit
  • POST /credit
  • POST /debit-reversal

REST v1 has path-based mutation direction. OpenPoly selects the endpoint from the operation type; the JSON request body does not include a direction field.

The shared adapter config also has a credit_reversal_path field for compatibility with the internal direction enum, but the current Mini App trade/redemption processor does not call POST /credit-reversal in production flows.

Mutation request body

All mutation endpoints use the same JSON body shape:

{
  "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": {}
}

OpenPoly sends every field below on every mutation request.

FieldTypeRequirementNotes
external_user_idstringRequiredStable operator-side user ID.
amount_minorintegerRequiredPositive amount in the currency's minor unit; exactly 0 only for a valid Polyflight loss credit. Current REST v1 operators use IDR.
currency_codestringRequiredCurrent REST v1 value: IDR; Polyflight requires IDR.
idempotency_keystringRequiredDurable deduplication key.
reasonstringRequiredBusiness reason from the Opinion or Polyflight reason catalogue below.
metadataobjectRequiredAlways sent; may be empty. Fields vary by flow and must be treated as additive.

external_user_id is required for debit, credit, and reversal paths. OpenPoly resolves it from the Mini App launch/session user and sends it again on each wallet mutation.

Required metadata by flow

Metadata is part of the production contract. Operators must accept additional fields without rejecting the request.

FlowReasonRequired metadataOptional additive metadata
Opinion buy debitmini_app_buy_orderorderId, marketIdoutcome
Opinion buy compensationmini_app_buy_compensationorderId, marketIdbalanceOperationId
Opinion sell creditmini_app_sell_orderorderId, marketIdoutcome
Opinion redemption creditmini_app_redemptionredemptionItemId, marketIdNone currently
Polyflight purchase debitmini_app_polyflight_purchaseproduct, operation, purchaseRequestId, flight/choice/native quote/timing/economy fieldsAdditive correlation fields
Polyflight purchase compensationmini_app_polyflight_purchase_compensationPurchase fields plus original debit referenceAdditive correlation fields
Polyflight win creditmini_app_polyflight_winCommon fields plus ticketId, original ticket quote, operation: "win", and result: "won"Additive correlation fields
Polyflight loss creditmini_app_polyflight_lossCommon fields plus ticketId, original ticket quote, operation: "loss", and result: "lost"; amount exactly zeroAdditive correlation fields
Polyflight cancelled-ticket refundmini_app_polyflight_refundCommon fields plus ticketId and original debit referenceAdditive correlation fields
The full-contract adapter test uses mini_app_wallet_adapter_test with test-only metadata. It is not a market trade or redemption and is exempt from this production matrix.
See Native wallet contract for exact Polyflight keys, amounts, and metadata names. Polyflight purchase and payout amounts are already native IDR and do not use an Opinion FX snapshot.

The zero-amount loss credit durably acknowledges a losing result without changing the balance or creating monetary accounting entries. It uses the existing credit/idempotency/reconciliation lifecycle, not a separate endpoint or webhook event. Only this narrowly defined Polyflight loss contract accepts zero; generic wallet mutations and adapter-test amounts remain positive. Historical losses are not backfilled, and existing planned/dispatched requests keep their original evidence.

Core rules

  • Every mutation must be idempotent.
  • Direct mutation response status is required and must be succeeded, failed, or pending. Other response fields are optional unless an endpoint page says otherwise.
  • Timeout, network failure, or an invalid HTTP 2xx response makes the primary mutation ambiguous. Without a valid REST v1 response body, HTTP 408, 429, or 5xx is also ambiguous; another HTTP 4xx is a failed mutation.
  • A valid response body with status = succeeded, failed, or pending is honored even on non-2xx. pending remains ambiguous.
  • Do not retry an ambiguous primary debit or credit automatically. An operator may already have applied it. Reconciliation may authorize a retry only with the original key.
  • Unknown wallet state must not be reported as success.
  • OpenPoly order state and wallet state are not one atomic transaction.
  • Manual review may be required when remote wallet result is unknown.
Authorization: Bearer {opera...ken}
X-Polynion-Request-Id: req_...
X-Polynion-Timestamp: 2026-06-18T00:00:00.000Z

Optional request signing may also be enabled with X-Polynion-Signature.

Dashboard setup

Operator admins configure the adapter in Operator Dashboard → Integrations → Wallet Adapter.

The dashboard stores:

  • base URL;
  • bearer token;
  • optional signing secret;
  • timeout and path overrides.

Bearer and signing secrets are encrypted before storage. After saving, OpenPoly only shows whether a secret is configured; plaintext and encrypted payloads are never returned by the API.

Validation tests

The dashboard exposes two validation modes:

  1. Connection test — calls only GET /balance for a supplied external user ID. It does not mutate wallet state.
  2. Full contract test — performs real wallet mutations against a dedicated test user and small amount. It validates balance, debit, debit reversal, credit, a second debit, and final balance. The final balance must equal the initial balance.

Full contract tests require explicit confirmation because compensation can fail or become unknown. If a compensation step fails, OpenPoly reports requires_manual_review so the operator can inspect and reconcile the remote wallet manually.

Copyright © 2026