API, events, and proof
API, events, and proof
Polyflight adds operator-scoped read resources and webhook events without changing the REST v1 wallet response contract. Private endpoints derive operator ownership from the authenticated API key or staff session; never send an operatorId to widen access.
Scopes
| Scope | Purpose |
|---|---|
polyflight:read | Polyflight overview, participating flights, tickets, and product activity. |
polyflight_terms:read | Current, scheduled, and historical commercial terms. |
polyflight_settlements:read | Provisional/final statements, statement exports, liabilities, and payment history. |
balance_operations:read | Wallet request/response evidence, including Polyflight source links. |
webhooks:read | Endpoint and delivery evidence for Polyflight events. |
These scopes are assignable but are not silently added to existing restricted keys or staff memberships. * can read operator-owned resources but does not create an operator terms-write, statement-finalize, payment-record, or manual-resolution endpoint.
Operator endpoint inventory
All routes are GET-only for operator reporting:
| Resource | GET paths | Required scope |
|---|---|---|
| Overview | /api/operator/polyflight/overview | polyflight:read |
| Participating flights | /api/operator/polyflight/flights, /flights/{flightIdOrCode}, /flights/export | polyflight:read |
| Tickets | /api/operator/polyflight/tickets, /tickets/{ticketId}, /tickets/export | polyflight:read and polyflight_terms:read |
| Wallet operations | /api/operator/polyflight/balance-operations, /balance-operations/{operationId}, /balance-operations/export | polyflight:read and balance_operations:read |
| Terms and history | /api/operator/polyflight/terms | polyflight_terms:read |
| Statements | /api/operator/polyflight/settlement-reports, /settlement-reports/{id}, /settlement-reports/export, /settlement-reports/{id}/export | polyflight_settlements:read |
| Payment history | /api/operator/polyflight/settlement-reports/payments, /settlement-reports/{id}/payments | polyflight_settlements:read |
| Webhook evidence | /api/operator/webhooks/deliveries?product=polyflight | webhooks:read and polyflight:read |
List filters are date_from, date_to, comma-separated status, search, page, and page_size; statement lists use pagination. Export returns CSV by default and canonical JSON with format=json; CSV uses spreadsheet-formula protection. All DTOs use camelCase and native aggregate amounts are decimal strings. Every response is tenant-derived and private, no-store.
Use Operator Dashboard → API Playground → Polyflight (/api-playground/polyflight) for source-backed examples. The Playground inherits the staff session and does not expose its bearer token. External integrations must call the same routes from their backend with an appropriately scoped API key.
The dashboard mirrors these resources at /polyflight, /polyflight/flights, /polyflight/tickets, /polyflight/balance-operations, /polyflight/settlement-reports, and /polyflight/terms.
Opinion metrics continue to exclude Polyflight native money unless an explicitly labelled all-products wallet audit is selected. Never add IDR amounts to Credits totals.
Webhook catalogue
Polyflight events use the existing signed envelope, signature verification, retry schedule, and event_id deduplication rules.
| Event type | Meaning |
|---|---|
mini_app.polyflight.purchase.processed | Debit and ticket admission completed; the immutable ticket exists. |
mini_app.polyflight.purchase.failed | Purchase reached a terminal failure without an admitted ticket; inspect compensation state when a debit may have applied. |
mini_app.polyflight.ticket.resolved | The global result classified the ticket and records the amount owed, if any. It does not assert wallet delivery. |
mini_app.polyflight.payout.pending | A winning payout is owed or requires follow-up. |
mini_app.polyflight.payout.processed | Winning wallet credit is confirmed. |
mini_app.polyflight.refund.pending | A cancelled-ticket full refund is owed or requires follow-up. |
mini_app.polyflight.refund.processed | Cancelled-ticket wallet credit is confirmed. |
mini_app.polyflight.purchase.compensated | An applied purchase debit with no admitted ticket was reversed. |
The signed envelope remains snake_case: event_id, event_type, operator_id, occurred_at, aggregate_type, aggregate_id, idempotency_key, and data. Polyflight data keys are camelCase:
| Family | Exact data fields |
|---|---|
purchase.processed | purchaseRequestId, ticketId, flightId, flightCode, status, currencyCode, stakeMinor, feeMinor, netStakeMinor, potentialPayoutMinor, choiceKind, choiceIndex, predictedSequence, economyVersion |
purchase.failed | purchaseRequestId, operatorId, operatorUserId, flightId, flightCode, status, failureReason |
ticket.resolved | ticketId, purchaseRequestId, flightId, flightCode, status, currencyCode, payoutMinor, obligationId |
| Payout/refund pending or processed; purchase compensated | obligationId, effect, purchaseRequestId, ticketId, flightId, flightCode, amountMinor, currencyCode, status |
ticketId and obligationId are nullable when the outcome/effect permits. Pending/manual-review payment events add failureReason; an admin-resolved processed event adds manualResolution; engine-created refund.pending adds voidCode.
Payload data is reconstructed from immutable native ticket, operation, obligation, and captured-terms rows. It does not revalue an amount with current FX or current terms.
Receivers must tolerate additive fields, branch only on known event_type, and deduplicate by event_id. Delivery replay may repeat an event; it never repeats or authorizes the underlying wallet mutation.
Comparison-only FX
A polyflight-v5 flight freezes one Indodax USDT_IDR observation before boarding. The committed valuation contains:
- policy
indodax-usdt-idr-v1; - source
indodaxand pairUSDT_IDR; - positive canonical rate with exactly 8 fractional digits and either
0or 1–12 integer digits (maximum serialized length 21); - source timestamp and observation correlation ID;
- observation age and
fallbackUseddisclosure.
A refresh targets five-minute freshness, but refresh failure may use the latest persisted valid observation. That cached fallback is frozen and disclosed; it is not refreshed at purchase, lock, result, payout, or replay. If no valid observation has ever existed, that flight remains Credits-capable but IDR entry is unavailable.
For one option, the verifier compares exact integer weight:
C = gross first-party Credits stake
I = gross IDR stake across all operators
R = canonical rate × 10^8
K = 10^8
comparisonWeight = C × R + I × 10,000 × K
Every option uses the same denominator, so ranking compares the integer weights directly. Commercial shares, ticket-fee earnings, and wallet-delivery status do not affect ranking. The rate is comparison evidence only: users are debited and credited directly in their original currency.
v5 mixed-money proof
New mixed-money-capable flights use:
- rules/protocol
polyflight-v5; - public package codec 4, domain
polyflight-public-package-v5; - snapshot codec 3, domain
polyflight-snapshot-v5; - lock package codec 4, domain
polyflight-lock-package-v5; - unchanged grouped
abcdOptions/efghOptionsandcount-v2strategy behavior.
The public package declares currencyCodes as exactly ['CREDITS'] or ['CREDITS', 'IDR'], stakeUnit: 'minor', and either the frozen valuation or null. null means Credits-only entry for that flight.
After landing, the anonymous v5 snapshot discloses each ticket's anonymous UUID, choice group/index, CREDITS or IDR, gross stake, fee, and potential payout. Per-option ranking totals publish totalStakeCredits and totalStakeIdr as decimal strings, plus ticket count and rank.
The public proof never contains operator ID, external user ID, linked Polynion user ID, wallet reference, balance, commercial terms, or settlement allocation. Native amounts are intentionally disclosed after landing so independent verifiers can recompute the committed mixed-money ranking and result.
Private operator ticket reports expose proofTicketId to correlate an owned v5 ticket with that anonymous UUID; it is null for legacy v4. The mapping remains private and is not itself added to the public proof.
The verifier checks native ticket economics, snapshot/public/lock digests, exact ranking, fallback ranks, unchanged strategy candidate, draw, selected branch, result, approach tracks, and owned-ticket classification. v1–v4 codecs and verifiers remain supported for their historical flights; v5 support is additive and does not rewrite old commitments or apply today's economy to an older proof.
