OpenPoly logo
Webhooks

Event types

Business events sent by OpenPoly to operator webhook receivers.

Event types

Current event list comes from packages/mini-app/src/types.ts.

Source-of-truth list

  • mini_app.login.succeeded
  • mini_app.trade.submitted
  • mini_app.trade.processed
  • mini_app.trade.failed
  • mini_app.trade.compensation_required
  • mini_app.redemption.processed
  • mini_app.redemption.failed
  • mini_app.payout.pending
  • mini_app.logout.requested
  • mini_app.session.expired
  • mini_app.polyflight.purchase.processed
  • mini_app.polyflight.purchase.failed
  • mini_app.polyflight.ticket.resolved
  • mini_app.polyflight.payout.pending
  • mini_app.polyflight.payout.processed
  • mini_app.polyflight.refund.pending
  • mini_app.polyflight.refund.processed
  • mini_app.polyflight.purchase.compensated

Payload reference

Current callback delivery reconstructs data at send time for aggregate_type = mini_app_order. This includes current durable status, failure reason and execution linkage; it is not a frozen event-time order snapshot. Preserve event_id deduplication even if a replay's reconstructed data differs. The Polyflight snapshot boundary is described separately below.

Trade order data fields

Trade events with aggregate_type = mini_app_order use this data family:

  • order_id
  • trade_id
  • executed_trade_id
  • operator_id
  • operator_user_id
  • polynion_user_id
  • external_user_id
  • market_id
  • side: buy or sell
  • outcome: yes or no
  • status
  • requested_external_amount_minor
  • requested_shares_micro
  • quoted_credits_equivalent
  • quoted_external_amount_minor
  • shares_micro
  • balance_spent
  • trade_fee
  • trade_fee_credits
  • currency_code
  • fx_rate_snapshot_id
  • failure_reason

Do not expect legacy credits_delta or external_amount_minor in trade callback data.

trade_fee is historical fee in integer minor units of currency_code, matching Operator Trade Fees view. trade_fee_credits is same fee denominated in Credits.

balance_spent is a shared historical field name, not a debit confirmation. For a buy it can contain the requested amount even on failure; for a sell it can describe the quoted/executed return. Interpret it with side, status and wallet evidence. shares_micro similarly does not by itself prove an executed trade.

mini_app.login.succeeded

Allowlisted event name for launch/session lifecycle. Current durable processor paths do not define an operator-actionable payload beyond the common envelope.

mini_app.trade.submitted

Allowlisted event name for order submission. Current durable trade processor callbacks use the processed, failed, or compensation-required terminal events below.

mini_app.trade.processed

Sent when trade completed successfully.

{
  "order_id": 123,
  "trade_id": 456,
  "executed_trade_id": 456,
  "operator_id": 12,
  "operator_user_id": 34,
  "polynion_user_id": 789,
  "external_user_id": "operator-user-123",
  "market_id": 51,
  "side": "buy",
  "outcome": "yes",
  "status": "processed",
  "requested_external_amount_minor": 10000,
  "requested_shares_micro": null,
  "quoted_credits_equivalent": 6340,
  "quoted_external_amount_minor": 10000,
  "shares_micro": 1234500,
  "balance_spent": 10000,
  "trade_fee": 39,
  "trade_fee_credits": 25,
  "currency_code": "IDR",
  "fx_rate_snapshot_id": 88,
  "failure_reason": null
}

mini_app.trade.failed

Sent for trade failure or a wallet/evidence hold such as manual_review. A rejected buy may have no trade, as below; an executed sell can retain a non-null executed_trade_id while its credit needs review. Do not infer “no trade executed” or “no wallet money moved” from the event name. Inspect side, source status, execution linkage and original wallet operation evidence.

{
  "order_id": 123,
  "trade_id": null,
  "executed_trade_id": null,
  "operator_id": 12,
  "operator_user_id": 34,
  "polynion_user_id": 789,
  "external_user_id": "operator-user-123",
  "market_id": 51,
  "side": "buy",
  "outcome": "yes",
  "status": "failed",
  "requested_external_amount_minor": 10000,
  "requested_shares_micro": null,
  "quoted_credits_equivalent": 6340,
  "quoted_external_amount_minor": 10000,
  "shares_micro": null,
  "balance_spent": 10000,
  "trade_fee": null,
  "trade_fee_credits": null,
  "currency_code": "IDR",
  "fx_rate_snapshot_id": null,
  "failure_reason": "OPERATOR_DEBIT_FAILED"
}

mini_app.trade.compensation_required

Sent when wallet debit happened but downstream completion needed manual or compensating handling.

{
  "order_id": 123,
  "trade_id": null,
  "executed_trade_id": null,
  "operator_id": 12,
  "operator_user_id": 34,
  "polynion_user_id": 789,
  "external_user_id": "operator-user-123",
  "market_id": 51,
  "side": "buy",
  "outcome": "yes",
  "status": "compensation_pending",
  "requested_external_amount_minor": 10000,
  "requested_shares_micro": null,
  "quoted_credits_equivalent": 6340,
  "quoted_external_amount_minor": 10000,
  "shares_micro": null,
  "balance_spent": 10000,
  "trade_fee": null,
  "trade_fee_credits": null,
  "currency_code": "IDR",
  "fx_rate_snapshot_id": null,
  "failure_reason": "DB_EXECUTION_FAILED_AFTER_DEBIT"
}

mini_app.redemption.processed

Sent when redemption or payout completed successfully.

{
  "redemption_item_id": 77,
  "run_id": 9,
  "redeem_request_id": 88,
  "operator_id": 12,
  "operator_user_id": 34,
  "external_amount_minor": 15000,
  "fx_rate_snapshot_id": 91
}

mini_app.redemption.failed

Sent when redemption flow failed before successful credit completion.

{
  "redemption_item_id": 77,
  "run_id": 9,
  "operator_id": 12,
  "operator_user_id": 34,
  "status": "manual_review",
  "failure_reason": "OPERATOR_CREDIT_FAILED"
}

mini_app.payout.pending

Allowlisted event name for payout follow-up. Current Mini App redemption processor emits mini_app.redemption.failed with status = manual_review for unresolved payout-side failures.

mini_app.logout.requested

Allowlisted event name for operator-side session cleanup. Current durable processor paths do not define an operator-actionable payload beyond the common envelope.

mini_app.session.expired

Allowlisted event name for launched Mini App session expiry. Current durable processor paths do not define an operator-actionable payload beyond the common envelope.

Polyflight event semantics

EventContract
mini_app.polyflight.purchase.processedNative debit and ticket admission completed. Do not debit from the webhook.
mini_app.polyflight.purchase.failedNo ticket was admitted. status can be failed, manual_review, or compensation_pending; reconcile the purchase debit instead of assuming whether money moved.
mini_app.polyflight.ticket.resolvedTicket outcome and payout amount are final; this does not assert wallet delivery. Newly settled losses have payoutMinor: 0 and a non-null delivery obligationId, not money owed.
mini_app.polyflight.payout.pendingA winning payout is owed, or its wallet credit is in manual_review.
mini_app.polyflight.payout.processedWinning wallet credit is confirmed.
mini_app.polyflight.refund.pendingA cancelled-ticket full refund is owed, or its wallet credit is in manual_review.
mini_app.polyflight.refund.processedCancelled-ticket wallet credit is confirmed.
mini_app.polyflight.purchase.compensatedAn applied debit with no admitted ticket was reversed.

The signed envelope stays snake_case; Polyflight data keys are camelCase. Initial pending events use status = "owed" and do not signal a failed credit. A later pending event with status = "manual_review" includes failureReason. Processed events confirmed from operator evidence add manualResolution: true.

New Polyflight event snapshots include operatorUserId, externalUserId, currencyCode, stakeMinor (gross debit or purchase quote), feeMinor (ticket fee or quote), netStakeMinor, and potentialPayoutMinor. Purchase failures without a ticket report quoted economics only; they do not claim a debit or earned fee. Admitted-ticket events additionally include feeBps, payoutMultiplier, commercialTermsVersionId, ticketFeeShareBps, and positionShareBps; the last two describe the captured operator commercial allocation, not extra fees charged to the user. A cancelled-ticket refund reverses the full gross stake, including its fee. A no-ticket compensation reverses the full applied debit, with no earned ticket fee.

amountMinor on a pending payout/refund or compensation failure is owed, not yet confirmed cash; on a processed payout/refund or compensated purchase it is confirmed wallet credit/reversal. payoutMinor on ticket resolution is the result (0 for a loss), not proof of credit. status belongs to the event-time snapshot: later payment or manual resolution does not change it on webhook replay. Event omissions on older deliveries mean unknown, never zero; read API, events, and proof for precise fields.

The eight Polyflight events are unchanged. A new loss's zero-amount /credit acknowledgement is tracked by the ticket/payment lifecycle and loss_credit wallet operation; no loss payout pending/processed callback exists. ticket.resolved remains authoritative regardless of acknowledgement status. Historical loss payloads can retain obligationId: null; webhook replay does not backfill a loss delivery or rewrite planned/dispatched wallet evidence.

Use event_id to deduplicate delivery. Wallet debit, credit, and reversal requests have separate financial idempotency keys; a webhook must never trigger a second money movement. See API, events, and proof for the source-backed payload families, full envelope example, and handling table.

Guidance

  • keep receiver tolerant to additive fields
  • branch only on known event_type
  • use event_id for dedupe
  • for current trade callbacks, prefer order_id, trade_id / executed_trade_id, operator_user_id, external_user_id, and market_id for support correlation
  • for current redemption callbacks, prefer redemption_item_id, run_id, redeem_request_id, operator_user_id, and external_amount_minor when present
  • treat failure_reason as operator-facing diagnostic string, not enum guaranteed forever
  • examples above are source-backed where current contract already defines fields explicitly
Copyright © 2026