Neun PredictionsGetting startedIntegration guideErrorsReference

Events: how delivery works today

status: written from ADR-062, ADR-006, ADR-007 and the code. The event list is generated below. [FO]

Events say what happened. They never move money.

Built

PartStateFlag
Outbox: events are written in the same database transaction as the change. A publisher sends them on.[BT]EVENTS_PUBLISHER (kafka or off, default off)
Integration log: one ordered stream per operator, a sequence number with no gaps.[FO]INTEGRATION_LOG_ENABLED
Signed webhooks: batches, retries, subscription suspended after 72 hours of failure. Secrets need WEBHOOK_SECRET_STORE=kv2.[FO]EVENT_SUBSCRIPTIONS_ENABLED
Test event: POST /v1/s2s/event-subscriptions/{id}/test sends one signed neun.predictions.subscription.test event to the subscription. The secret is never returned. Another operator gets 404. A disabled subscription gets 422 SUBSCRIPTION_NOT_ACTIVE. Unsafe addresses are refused. Each subscription has a test budget (429).[FO]EVENT_SUBSCRIPTIONS_ENABLED
Pull feed: GET /v1/s2s/integration-events?after=&limit=&types=&wait= with scope events:read.[BT]none

Pull feed example parameters from the OpenAPI file: after=48212, types=order.completed,sell.failed. A cursor that is too old returns 410 CURSOR_EXPIRED. Delivery is at least once. Remove duplicates by event id.

Not built

PartState
Kafka topic for operators (delivery mode C)[NB] Open: N14
Bootstrap exports (POST /v1/s2s/exports)[NB] Open: N14

Envelope, order and partition keys

Each event is a CloudEvent 1.0 in JSON. Every event has neunpartitionkey (the data field named in the AsyncAPI x-partition-key), neunresourceversion and neunsequence (no gaps, per operator).

Order holds per partition key. Apply an event only when its neunresourceversion is higher than the one you already hold. Do not order by arrival time.

Partition keyEvents
orderIdorder.accepted, order.cancelled, order.completed, order.expired, order.failed
marketIdmarket.resolved, correction.applied
operatorIdoperator.wallet.health_changed
positionIdposition.opened, position.settled, position.voided
sellIdsell.completed, sell.failed
settlementLineIdsettlement.line_settled
receivableIdplayer.receivable.opened, player.receivable.closed
operationIdwallet.operation.escalated, wallet.operation.finalized
writeOffIdwrite_off.requested, write_off.approved

Exception: correction.applied is keyed by correctionId (ADR-062:95); the internal Kafka key stays marketId (ADR-006:32). Order the events of one correction by neunresourceversion.

order.cancelled and order.expired are defined but not emitted in R1.

Delivery notes

Server SDK helpers (on a branch, not released)

parseEventBatch checks the signature on the raw bytes, then reads the batch. It refuses a body over 1,048,576 bytes and a batch over 500 events. pollEvents reads the pull feed and never follows a redirect, so the token cannot go to another host. Both are in @neungaming/predictions-server on branch feat/v20-r15-serversdk: on branch, not released. See what is released.

Contract file: packages/contracts/asyncapi/events-v1.json (AsyncAPI). The generated list of event types follows.

Event catalogue

status: generated from packages/contracts/asyncapi/events-v1.json (20 types). [FO] Event Connector flags default off. Types with x-status defined-not-emitted have no producer. Dedupe by id; delivery is at least once.

correction.applied

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.correction.applied. Partition key: correctionId.

fieldtyperequired
changeTypestring ["APPLIED"]yes
correctionIdstringyes
marketIdstringyes
originstring ["STAFF","SOURCE"]yes
statestringyes
newResolutionVersion?yes

market.resolved

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.market.resolved. Partition key: marketId.

fieldtyperequired
changeTypestring ["RESOLVED"]yes
marketIdstringyes
resolutionIdstringyes
resolutionVersionintegeryes
outcomestringyes
statestring ["RESOLVED_FINAL","VOIDED"]yes
settlementVectorarrayyes

operator.wallet.health_changed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.operator.wallet.health_changed. Partition key: operatorId.

fieldtyperequired
changeTypestring ["UP","DEGRADED"]yes
operatorIdstringyes
statusstring ["UP","DEGRADED"]yes

order.accepted

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.order.accepted. Partition key: orderId.

fieldtyperequired
changeTypestring ["ACCEPTED"]yes
orderIdstringyes
quoteIdstringno
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
failureCodestring,nullno
totalDebitobjectno

order.cancelled

x-status: defined-not-emitted. CloudEvents type neun.predictions.order.cancelled. Partition key: orderId.

fieldtyperequired
changeTypestring ["CANCELLED"]yes
orderIdstringyes
quoteIdstringno
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
failureCodestring,nullno
totalDebitobjectno

order.completed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.order.completed. Partition key: orderId.

fieldtyperequired
changeTypestring ["COMPLETED"]yes
orderIdstringyes
quoteIdstringno
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
failureCodestring,nullno
totalDebitobjectno

order.expired

x-status: defined-not-emitted. CloudEvents type neun.predictions.order.expired. Partition key: orderId.

fieldtyperequired
changeTypestring ["EXPIRED"]yes
orderIdstringyes
quoteIdstringno
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
failureCodestring,nullno
totalDebitobjectno

order.failed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.order.failed. Partition key: orderId.

fieldtyperequired
changeTypestring ["FAILED"]yes
orderIdstringyes
quoteIdstringno
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
failureCodestring,nullno
totalDebitobjectno

player.receivable.closed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.player.receivable.closed. Partition key: receivableId.

fieldtyperequired
changeTypestring ["CLOSED"]yes
receivableIdstringyes
correctionLineIdstringno
playerLinkIdstringno
amountobjectno
closedReasonstringno

player.receivable.opened

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.player.receivable.opened. Partition key: receivableId.

fieldtyperequired
changeTypestring ["OPENED"]yes
receivableIdstringyes
correctionLineIdstringno
playerLinkIdstringno
amountobjectno
closedReasonstringno

position.opened

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.position.opened. Partition key: positionId.

fieldtyperequired
changeTypestring ["OPENED"]yes
positionIdstringyes
orderIdstringyes
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestringyes
sharesstringyes
averageEntryPricestringyes
costBasisobjectyes
feesobjectyes
maxSettlementProceedsobjectyes

position.settled

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.position.settled. Partition key: positionId.

fieldtyperequired
changeTypestring ["SETTLED"]yes
positionIdstringyes
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestring ["SETTLED","VOIDED"]yes
settlementobjectyes

position.voided

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.position.voided. Partition key: positionId.

fieldtyperequired
changeTypestring ["VOIDED"]yes
positionIdstringyes
playerLinkIdstringyes
marketIdstringyes
outcomeIdstringyes
statestring ["SETTLED","VOIDED"]yes
settlementobjectyes

sell.completed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.sell.completed. Partition key: sellId.

fieldtyperequired
changeTypestring ["COMPLETED"]yes
sellIdstringyes
quoteIdstringyes
positionIdstringyes
playerLinkIdstringyes
kindstring ["FULL","PARTIAL"]yes
statestringyes
sharesstringyes
filledShares?yes

sell.failed

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.sell.failed. Partition key: sellId.

fieldtyperequired
changeTypestring ["FAILED"]yes
sellIdstringyes
quoteIdstringyes
positionIdstringyes
playerLinkIdstringyes
kindstring ["FULL","PARTIAL"]yes
statestringyes
sharesstringyes
filledShares?yes

settlement.line_settled

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.settlement.line_settled. Partition key: settlementLineId.

fieldtyperequired
changeTypestring ["SETTLED"]yes
settlementLineIdstringyes
settlementRunIdstringyes
positionIdstringyes
outcomestringyes
settlementProceedsobjectyes
feeRefundobjectyes

wallet.operation.escalated

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.wallet.operation.escalated. Partition key: operationId.

fieldtyperequired
changeTypestring ["ESCALATED"]yes
operationIdstringyes
typestringyes
statusstring ["SUCCEEDED","REJECTED","ESCALATED"]yes
amountobjectyes
rejectionCodestring,nullyes
businessobjectyes

wallet.operation.finalized

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.wallet.operation.finalized. Partition key: operationId.

fieldtyperequired
changeTypestring ["SUCCEEDED","REJECTED"]yes
operationIdstringyes
typestringyes
statusstring ["SUCCEEDED","REJECTED","ESCALATED"]yes
amountobjectyes
rejectionCodestring,nullyes
businessobjectyes

write_off.approved

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.write_off.approved. Partition key: writeOffId.

fieldtyperequired
changeTypestring ["APPROVED"]yes
writeOffIdstringyes
caseIdstringno
receivableId?yes
brandId?no
amountobjectno
amountUsdstringyes
reasonCodestringno
seniorRequiredbooleanno
bandstringno
expiresAtstringno
signersintegerno
receivableOutstanding?no
approvedAtstringno

write_off.requested

x-status: not set in the document (no claim made). CloudEvents type neun.predictions.write_off.requested. Partition key: writeOffId.

fieldtyperequired
changeTypestring ["REQUESTED"]yes
writeOffIdstringyes
caseIdstringno
receivableId?yes
brandId?no
amountobjectno
amountUsdstringyes
reasonCodestringno
seniorRequiredbooleanno
bandstringno
expiresAtstringno
signersintegerno
receivableOutstanding?no
approvedAtstringno