Pick responses omit internal selection diagnostics
Pick of the Day and its archive now focus on the published selection, game, holder positions and grades, prices, results, and execution permission. Internal selection diagnostics are no longer included in customer responses.Before this change, responses could include internal selection evidence. Published prices, outcomes, identity, access metadata, and historical proof records keep their contracts.Breaking for clients that require internal selection diagnostics.What to change: Use the documented customer fields and honor the returned entry authorization, its expiry, and a current executable order book for your order size.
Pick of the Day admits eligible selections in a neutral order
Pick of the Day now gives new selections a neutral presentation order.
publication_order gives their presentation order, and is_free_selection names the selection available to a signed-in free account.Before this change, eligible selections did not use a neutral presentation order. Standing selections retain their identity, slot, and release schedule; later additions fill available slots.The daily limit, release timing, and Free/Pro access remain intact. Historical order, dated links, and proof bytes remain unchanged, and a thin day still returns fewer selections.Backward compatible. The stable-ID and access fields keep the shapes shipped in SDK 0.15.0 and MCP 2.13.0.What to change: Use pick_id for identity, publication_order for presentation, and is_free_selection for access. Stop treating a slot number or list position as a quality recommendation.SDK 0.15.0 and MCP 2.13.0 ship stable pick lookups
SDK
0.15.0 and MCP 2.13.0 are published on npm. The SDK includes getPickOfTheDayLedgerEntry(pickId); the local MCP package includes get_pick_of_the_day_ledger_entry in its 48-tool catalog.The earlier setup used MCP 2.12.7, which did not include the stable-ID tool. MCP setup now pins 2.13.0, and the TypeScript guide shows the released SDK helper. The corresponding REST route is available without a key; MCP tool calls retain their Pro authentication.Backward compatible. Existing methods, tools, and legacy proof verification remain available.What to change: Install @0xinsider/sdk@0.15.0 or @0xinsider/mcp@2.13.0 to use the stable-ID lookup. Keep pick_id as a decimal string and dispatch committed proofs by commitment_version.Stable Pick of the Day ledger lookups honor public keyless access
GET /api/v1/pick-of-the-day/ledger/{pick_id} now honors its documented public access for concrete pick IDs. The entry keeps the same side and nonce withholding as the full ledger.Before this change, authorization compared a concrete URL with the literal route template. A keyless lookup could return 401 before the public handler ran.A malformed ID returns 400; a missing or unpublished pick returns 404. Declared public route parameters match exactly one nonempty path segment.Backward compatible. This restores the documented keyless contract. Protected routes keep their authentication requirements.What to change: No API key is needed for this REST lookup or the SDK helper. Keep pick_id as a decimal string. MCP tool calls retain their Pro authentication.Games report Started for Dota matches with a settled map winner
GET /api/v1/games and GET /api/v1/games/{event_slug} now report status.provider_status: "Started" for Dota matches whose scheduled kickoff has passed, whose full-match winner remains unsettled, and whose settled map winner proves play began while live status is unavailable.Before this change, these matches retained status.provider_status: "Scheduled", status.state: "scheduled", and status.match_status: "scheduled". They now report status.state: "unknown" and omit status.match_status; status.live remains false. Started confirms earlier play, not that the match is currently live.Backward compatible. Existing response fields and the state vocabulary keep their shapes. No new field or state value is added.What to change: Nothing. Use status.live to identify confirmed live play.Pick of the Day adds stable IDs and versioned commitments
Pick of the Day, its archive, and its ledger now expose stable identity and explicit access metadata.Before this change, you had to identify a pick by its date and
pick_rank, and infer the free selection from that slot. New fields separate identity from presentation:pick_idis a positive decimal string that identifies the stored pick.publication_orderdescribes the compatibility order. It does not claim a quality score.is_free_selectionidentifies the selection available to signed-in free accounts.supersedes_pick_idnames a replaced selection, or isnullwhen there is no replacement.
GET /api/v1/pick-of-the-day/ledger/{pick_id} returns one published ledger entry by its stable ID. It is public, and live entries retain the ledger’s side and nonce withholding.Committed ledger entries declare commitment_version. Version 1 retains the original eight-field payload and proof bytes. Version 2 replaces payload pick_rank with the decimal-string pick_id and adds integer version: 2; every other canonical serialization rule stays the same. Uncommitted disclosures carry no commitment version and make no proof claim.Published SDK 0.15.0 includes getPickOfTheDayLedgerEntry(pickId), and MCP 2.13.0 includes get_pick_of_the_day_ledger_entry with a pick_id argument. The REST read and SDK helper need no key; MCP tool calls keep their Pro authentication.Backward compatible. Existing fields and routes remain available; pick_rank is a compatibility slot, and historical commitments keep version 1.What to change: Store and link picks by pick_id. Read is_free_selection for access, and dispatch proof verification by commitment_version; reject an unknown version instead of guessing.Pick of the Day updates the limits on future entry authorizations
GET /api/v1/pick-of-the-day updates the price limits returned in newly issued entry_authorization objects. Your order must honor the returned limit and expiry, and still needs a current executable book check for its size. Fees are additional.Before this change, new automated entry permissions had a different price limit. Historical authorization records keep their original values; use the authorization returned for your request rather than deriving a permission from an older record.Fewer picks or authorizations may be available. Neither a pick nor its authorization provides a fair probability or a profitability guarantee.Backward compatible. Response fields keep their existing shapes. Fewer future picks or authorizations may be available.What to change: Honor the returned entry_authorization.max_entry_price rather than calculating your own ceiling from the ask. Skip an automated entry when its authorization is missing or expired.The TypeScript SDK is available on npm as @0xinsider/sdk 0.14.0
@0xinsider/sdk 0.14.0 is available with npm install @0xinsider/sdk. It includes typed API calls, cursor pagination, resumable SSE streams, typed errors, and webhook signature checks. The package needs Node.js 18 or later and ESM, includes TypeScript declarations, and has no runtime dependencies.Before this release, you had to clone and build the public source repository. The npm release carries provenance from that repository’s GitHub publication workflow.Backward compatible. This adds a registry installation path for the existing SDK.What to change: Install @0xinsider/sdk from npm instead of a local build directory. You can use OxinsiderApiClient.sandbox() without an API key.MCP output schemas describe null with explicit JSON Schema alternatives
POST /api/v1/mcp now publishes tools/list output descriptors that express nullable values as anyOf alternatives, including type: "null". The get_pick_of_the_day tool’s outputSchema describes sports_context.yes_team and sports_context.no_team as a team object or null, matching its existing response.Before this change, those team schemas combined allOf with nullable: true and no type. JSON Schema 2020-12 consumers using Ajv could reject the descriptor during compilation, before validating a tool result.Backward compatible. The explicit null alternatives are additive descriptions of existing response values. Response fields, tool names, authentication, and scopes keep their contracts.What to change: Refresh cached tools/list descriptors and recompile your output validators. Your tool calls stay the same.CLI response-body network failures retain exit 7 and login backoff
The CLI now uses exit
7 for a network failure while reading a response body, as it already does before headers arrive. OAuth body timeouts use the same request deadline, and device login keeps its network backoff. Invalid JSON or response schemas remain exit 6, and interruption remains 130.The TypeScript SDK adds ResponseBodyReadError for an interrupted JSON or text body, with responseStatus, phase: "response_body", and the original transport cause. SDK deadlines retain RequestTimeoutError, and caller cancellation retains its exact reason. Registration, refresh, revocation, and SDK body reads gain no automatic retry.Compatibility: The CLI follows its documented exit codes. The SDK replaces untyped native body-read rejections with the new error class, so consumers that branch on a native error class should handle ResponseBodyReadError.What to change: Upgrade the scoped CLI runtime or the bare 0xinsider launcher installation, and handle the SDK class when using the updated source. A lost write response leaves its outcome uncertain; reconcile the operation before retrying.RSS discovery omits a modification date that does not cover pick updates
GET /schemamap.xml now omits lastmod for its /feed.xml entry. The other feed entries keep their modification dates.Before this change, the RSS date covered authored article publication dates but missed pick publication and settlement updates. Consumers could treat a changed feed as unchanged.The RSS URL, item identifiers, publication dates, and atom:updated values stay the same.Backward compatible. The Schema Feed specification makes lastmod optional.What to change: When the RSS entry has no lastmod, fetch the feed instead of comparing its previous modification date.Public page Markdown keeps article content and reports missing or unsupported formats
Public article pages under
/learn, /research, and /compare requested with Accept: text/markdown now return the same article, title, authored dates, and canonical link as their existing .md URL.Before this change, negotiation could replace an article with generic site text and return 200 for a missing content slug. Missing content now keeps 404, and a page without a Markdown representation returns 406 when the request does not accept HTML.Mixed Accept values select an available representation using the existing quality and wildcard rules. Curated agent documents, document-only .md resources, APIs, and authenticated pages keep their existing contracts.Breaking for clients relying on generic Markdown at arbitrary page URLs. Existing article .md URLs and curated documents remain compatible.What to change: Accept 404 for missing content and 406 for an unsupported representation. Accept text/html as a fallback for pages without Markdown, or use the documented API for dynamic data.OAuth refresh keeps the existing scopes when scope is blank
POST /oauth/token now treats an empty or whitespace-only scope as omitted when grant_type is refresh_token, preserving the existing grant’s scopes.Before this change, a blank scope rotated the grant with no scopes, so the new access token could not use routes that require read or another scope.An explicit nonempty scope still narrows the grant to the requested scopes. Asking for a scope outside the existing grant still returns invalid_scope.Backward compatible. Omitted and valid nonempty scope requests keep their behavior. Token lifetimes and rotation stay the same.What to change: Nothing. Omit scope when refreshing unless you intend to narrow the grant; a blank value now preserves it too.Checkpointed TypeScript streams stop cleanly after cancelled callback failures
The TypeScript SDK’s
consumeStreamCheckpointed now returns cleanly when caller cancellation accompanies an awaited event, resync, or checkpoint callback failure, including with maxHandlerRetries: 0 or an exhausted retry budget.Before this change, those failures could consume the handler retry budget and throw StreamHandlerFailedError during ordinary shutdown.Failed work never advances the checkpoint. Successful callbacks and their durable checkpoint write still commit before returning after cancellation. onHandlerError retains the original callback error, reports willRetry: false, and adds optional cancelled: true; that flag means cancellation stopped the consumer, without claiming it caused the callback failure. The 1-based attempt still describes consecutive failures at that sequence.Backward compatible. The optional diagnostic field is additive. Retry and replay behavior without cancellation is unchanged. onHandlerError must still return synchronously without throwing.What to change: Rebuild from the updated public SDK source. If your diagnostic treats every willRetry: false as exhaustion, check cancelled to distinguish a clean shutdown. The standalone SDK’s first npm release remains pending.TypeScript stream reconnects honor the terminal error's retry_at
The TypeScript SDK’s
streamFeedResilient and consumeStreamCheckpointed now wait until a retryable terminal error’s retry_at before reconnecting when no usable HTTP Retry-After is available.Before this change, the SDK preserved retry_at on the error but used local backoff, which could reconnect before the server’s published recovery time.HTTP Retry-After remains preferred. Absolute times use your machine’s clock; keep it synchronized with the server. Past times permit an immediate retry, missing or invalid timing uses local backoff, and retry: false still stops permanently. The same maxRetryAfterMs ceiling, deferred-retry checkpoint, and cancellation behavior apply.Backward compatible. No public signature changes.What to change: Rebuild from the updated public SDK source. Keep your clock synchronized and schedule StreamRetryDeferredError.retryAt when a wait exceeds your ceiling. The standalone SDK’s first npm release remains pending.TypeScript export helpers release object responses they cannot return
The TypeScript SDK in the public source and bundled with
@0xinsider/mcp 2.12.6 now cancels an export object response when downloadTraderExport or downloadWhaleDataset cannot return it. It waits up to 2 seconds for cleanup and preserves the original failure, with cleanup rejection or unknown-completion information in its cause.Before this change, a failed object HTTP response or an error while preparing its stream could leave an unread body or reader unreleased.Successful downloads still stream, checksum defaults remain unchanged, and there is no new default download deadline. After a helper returns, you own consuming or cancelling response.body.Backward compatible. Method signatures and ordinary HTTP error class, status, and message remain unchanged. An existing cause is retained with cleanup information; a thrown value that cannot carry a cause is retained inside an AggregateError.What to change: Rebuild from the updated public SDK source, or update to @0xinsider/mcp 2.12.6 after publication. Continue consuming or cancelling returned bodies, and inspect a failure’s cause when diagnosing cleanup. The standalone @0xinsider/sdk first npm release remains pending.Remote MCP publishes result schemas and OAuth account-linking metadata
POST /api/v1/mcp now publishes result schemas and OAuth requirements in its remote tool descriptors. An OAuth call refused for a missing scope also includes an account-linking challenge.outputSchemadescribes each tool’s successfulstructuredContentwith required result fields, or its existingstructuredContent.errorobject.securitySchemesdeclarestype: "oauth2"withreadplus any scope required by the tool’s REST route._meta.securitySchemesrepeats the same schemes for clients that read this metadata field.- An insufficient-scope tool error adds
_meta["mcp/www_authenticate"], an array of Bearer challenges witherror="insufficient_scope",scope,resource_metadata, anderror_description. A host can use it to request the required authorization before retrying.
structuredContent without an output schema, their OAuth requirements were implicit, and insufficient-scope tool errors carried no account-linking hint.Backward compatible. The remote server still has 47 tools. Successful payloads, API-key access, paid-access requirements, and unauthenticated 401 discovery keep their contracts; the stdio package does not change.What to change: Nothing for clients using an API key or an OAuth grant with the required scope. Refresh cached tools/list descriptors to read the new fields; OAuth hosts can use the linking challenge to obtain the missing scope, then retry the refused call.SDK caller cancellation preserves its reason instead of becoming the SDK timeout
@0xinsider/sdk source and the SDK bundled in @0xinsider/mcp 2.12.5 preserve the exact reason from a caller’s cancellation signal on API calls. Only the SDK’s own deadline rejects with RequestTimeoutError and its configured timeoutMs; whichever signal aborts first determines the rejection through fetch, body consumption, and retry backoff.Before this change, a caller’s AbortSignal.timeout(2000) could be reported as the default 15-second SDK timeout. Custom caller reasons named TimeoutError could be wrapped the same way, even though the caller cancelled first.Backward compatible. Public signatures and retry eligibility stay the same. API cancellation starts no further request attempt; signed object downloads retain their separate timeout and error behavior.What to change: Match RequestTimeoutError for the SDK deadline and handle your caller signal’s reason separately. The first standalone npm publication remains pending; build the public source package until it is published.potd-trader 0.3.4 runs directly on Windows with the same CLI and ledger
potd-trader 0.3.4 runs directly in PowerShell on Windows x64, with Python 3.12.4 or newer. The same repository, commands, configuration, and v1 JSON ledger work on Windows, macOS, and Linux.Before this release, Windows required WSL because the trader used POSIX file locks. Windows now uses local file locks, flushes file contents, and atomically replaces ledger and control files; it does not flush their containing directory, so a hard power loss can lose the latest reservation or stop flag.On Windows, init limits its new folder to your current user account and administrators, and files inherit that folder’s access permissions.Backward compatible. Existing configuration and ledger formats stay the same. The live switch, stop flag, duplicate checks, price limits, and daily cap still apply.What to change: Follow the installation guide. Stop every old watcher, including WSL instances, before upgrading; keep one configuration and ledger in a private local folder outside OneDrive or network drives. Check status and run --dry-run before enabling live trading, and never run Windows and WSL instances for the same wallet together.Cold SSE replicas resume after a confirmed feed sequence reset
GET /api/v1/stream emits one existing resync marker with completeness.status: "reset" and continues at the new sequence after the feed bridge confirms a counter reset. This also works when the server has replayed retained history but has not yet forwarded a live frame.Before this change, that server could discard new events until their IDs reached the old replay head. Ordinary replay overlap still produces no reset marker, and losing the first reset envelope does not discard the bridge’s reset signal.Backward compatible. Event payloads, SSE IDs, privacy checks, and the existing marker format stay the same.What to change: Nothing. Continue handling resync by refreshing current state and storing the marker’s ID before processing later events.SDK pagination progress counts delivered pages and preserves the retry cursor
@0xinsider/sdk now updates PaginationProgress.pagesFetched before delivering each validated page. An early consumer return retains the page count and nextCursor; stoppedBy is set only when that delivered page exhausts the collection or reaches maxPages.Before this change, an early exit could report zero pages, a later failure could leave the previous request cursor, and a reused progress object could retain an old stop reason. New iterations reset owned progress fields, and failures or between-page cancellation publish the same retry cursor and completed-page count as paginationResumePoint(error).Page counts describe pages delivered, not items processed or persisted. Original errors, cursor validation, retry policy, and public signatures are preserved.Backward compatible. This fixes existing progress and resume diagnostics.What to change: Read progress after a delivered page or failure. Continue from nextCursor after an early return or max_pages, or from paginationResumePoint(error).cursor after a failure. The first npm publication remains pending; the fix is included in the public source package.Trader export download links expire within the file's retention window
GET /api/v1/trader/{address}/export/download returns a signed URL valid for at most 1 hour and no later than the job’s expires_at. If less than 1 whole second remains when signing, the route returns 410 with error.reason: "export_expired".Before this change, the URL was signed for 1 hour even when the export’s retention window ended sooner. A client could receive a still-valid link to a file that had already been retired.Backward compatible. This fixes the existing retention boundary; routes, response fields, and error codes stay the same. Links requested near expiry can last less than 1 hour.What to change: Use the SDK download target’s expiresAt or the job’s expires_at rather than assuming 1 hour. Request a fresh URL while the job is retained, and submit a new export after 410 export_expired.Details: issue #19988 and PR #19994.Market-holder cursors preserve your position when page size changes
GET /api/v1/market/{condition_id}/holders now returns cursors that preserve the next holder’s position when you change limit. Each new cursor belongs to the same market, outcome, and min_grade; using it with different filters returns 400 bad_request with param: "cursor".Before this change, changing limit multiplied the cursor’s page number by the new page size, which could skip or repeat holders even when the roster had not changed. The roster remains current state, so a refresh between pages can still change its membership or order.Backward compatible. Existing page-number cursors remain accepted without a scheduled retirement; keep their original limit until the next response returns a new cursor. Raw condition IDs and their mkt_ forms identify the same market; omitted filters match their documented defaults.What to change: Send next_cursor unchanged with the same market and filters. Restart without a cursor when you want different filters. You may change limit with new cursors.MCP keeps structured timeout and network errors through the complete response body
@0xinsider/mcp 2.12.4 returns structured request_timeout errors when its deadline expires before headers or while reading the response body. Other network and body-read failures return transport_error.Before this change, a body-read failure escaped the structured error handler and appeared only as unexpected-error text. Errors now retain the received HTTP status, request ID, and validated retry delay when headers arrived. Before headers, those facts remain absent.Fully read REST and non-JSON errors retain their existing classifications. The default 15-second deadline and explicit timeout opt-out stay the same. Neither error automatically retries a request; a POST whose response was lost may have committed.Backward compatible. Tool arguments and successful responses stay the same; network failures gain structured fields.What to change: Upgrade to @0xinsider/mcp 2.12.4 once published. Handle transport_error and request_timeout, and check a mutation’s outcome before repeating it.MCP retry guidance validates seconds and HTTP dates consistently
@0xinsider/mcp 2.12.3 uses one validated Retry-After delay in error text and structuredContent.error.retry_after_seconds. Whole non-negative seconds and valid HTTP dates are accepted. A future date becomes whole seconds rounded upward from header receipt; a past date becomes zero.Before this change, an HTTP date was missing from the structured field and appeared with a seconds suffix in the text. Negative values and numeric prefixes such as 10junk could become retry instructions.Missing, malformed, fractional, or unsafe numeric values now provide no retry instruction. Integer delays are bounded by Number.MAX_SAFE_INTEGER. The REST retry_at stays separate, and the stdio client still makes no automatic retries.Backward compatible. No tool argument or successful response changes.What to change: Upgrade to @0xinsider/mcp 2.12.3 once published. Schedule retries only when a valid delay or REST retry_at is supplied.SSE reconnects discard replay overlap without a false reset
GET /api/v1/stream now discards live frames already covered by reconnect replay without emitting a resync marker or sending those events again.Before this change, frames queued during replay could be mistaken for a sequence reset, causing duplicate events with lower SSE IDs. Genuine sequence resets still emit one resync marker with completeness.status: "reset", followed by events from the new sequence.Backward compatible. Event payloads, filters, authentication, and the reset marker format are unchanged.What to change: Nothing. Continue handling resync markers and using durable event IDs to deduplicate across genuine resets.potd-trader 0.3.3 removes pick rank filters and numbered labels
potd-trader 0.3.3 considers every released, otherwise eligible Pick of the Day selection at the same unit size. It no longer accepts MIN_RANKS or MAX_RANKS as trading filters, and its pick labels no longer display rank numbers. The API’s existing slot is still used internally to prevent duplicate orders.Before this release, explicitly configured rank ranges could exclude otherwise eligible picks. The daily cap, price and kickoff checks, live control, and duplicate protection still apply; the default 25 pUSD cap still funds five 5 pUSD picks.Breaking for users who relied on an explicit rank range: those settings are ignored and every eligible pick can be considered within the existing cap.What to change: Check potd-trader status, review your unit size and daily cap, and run a dry run before restarting. Stop live trading before using potd-trader size, which removes obsolete rank lines from .env. The main product’s rank-free identity and proof migration is tracked separately.potd-trader 0.3.2 includes every pick and asks for unit size and daily cap
potd-trader 0.3.2 makes all 10 Pick of the Day slots eligible at one unit size by default. Its init command asks for unit size and daily cap, and the new size command changes both in a stopped existing setup. status, run, and watch show budget capacity and warn when the cap cannot cover 10 picks.Before this release, the trader defaulted to ranks 1 through 6 and a 25 pUSD cap, which funded only five 5 pUSD orders. A one-shot run still sees only released picks; watch handles later releases. When the cap cannot fund all eligible picks, release time sets priority, with token ID breaking ties; each cap skip is shown.Breaking for users who relied on the default rank 1 through 6 filter: new setups include ranks 7 through 10. Existing explicit MIN_RANKS and MAX_RANKS settings remain in force, and existing daily caps do not rise automatically.What to change: Review your unit size and cap with potd-trader status. Stop the watcher and turn live trading off before using potd-trader size; remove old rank settings only if you want every pick.The TypeScript SDK rejects blank or overlong Idempotency-Key values before fetch
The
@0xinsider/sdk source build now rejects an effective Idempotency-Key that is empty after trimming or exceeds 255 UTF-8 bytes, before sending a request. Validation applies to both headers and idempotencyKey, including with maxRetries: 0; the explicit option overrides the custom header.Before this change, a blank header could enable retries even though the API treated it as no replay key. Omitting the key still sends a supported mutation once, and valid keyed retries keep the same key and body.Breaking for callers supplying invalid keys: these calls now fail locally. Valid and omitted keys retain their existing behavior. npm publication remains pending; use the documented source installation.What to change: Supply a stable nonempty key on a supported webhook mutation and reuse it when reconciling an unknown outcome, or omit the key for a single attempt. Do not send a blank value.TypeScript SDK errors retain Retry-After and X-Request-ID response headers
The
@0xinsider/sdk source build now retains parsed Retry-After on every HTTP error, including generic 502/504 errors and plain-text or empty bodies. requestId uses JSON meta.request_id first, then a nonempty received X-Request-ID header; a header does not create a synthetic meta object.Before this change, generic errors could discard retry guidance, and responses without JSON metadata lost their request ID. Error classes, raw bodies, automatic retry eligibility, and mutation replay requirements are unchanged.Backward compatible. Existing constructor and factory calls still work. npm publication remains pending; use the documented source installation.What to change: Read retryAfterSeconds and requestId from OxinsiderApiError when handling a received HTTP failure. A retry header does not make a mutation safe to repeat.Local MCP Explore results keep request and cache metadata
@0xinsider/mcp 2.12.2 includes the API’s meta object in stdio explore_markets results.Before this change, the local MCP adapter dropped meta. The REST Explore endpoint and remote MCP already returned it.request_ididentifies the API request.cachedsays whether the API served a cached result.cache_age_sremains absent when the API does not know the cache age. Other metadata fields pass through unchanged.
WebSocket controls keep working during slow writes and price recovery
The WebSocket hub (
/api/ws) now handles close, unsubscribe, credential expiry, and shutdown while a slow connection is writing or recovering prices. Latest queued quotes stay ordered, and a REST snapshot cannot replace a side that already has a streamed price.Before this change, a continuously replenished price buffer could postpone those controls. Protocol ping timeouts now count pings after they are written, rather than while they are still queued.A connection that exhausts the bounded output queue closes with send_backpressure. Retirement during an unfinished write can end the transport without a close frame.Backward compatible. Frame fields, topic limits, and authentication requirements are unchanged.What to change: Nothing. Keep your existing reconnect and resubscribe handling, including transport EOF without a close reason.SSE replay checks ongoing authorization before sending more protected events
GET /api/v1/stream now applies ongoing authorization to replay as well as live delivery. A paused response checks due authorization and connection closure before sending another protected replay event.Before this change, buffered replay could continue before those checks ran. A terminal error uses the last sequence the connection handled, and already-delivered bytes cannot be recalled. A healthy paused response can resume after fresh authorization; protected delivery waits for that check.Backward compatible. The event format, terminal error format, and reconnect headers are unchanged.What to change: Nothing. Keep reading terminal error frames and reconnect using Last-Event-ID as before.Export artifacts return the storage ETag without XML escapes
GET /api/v1/datasets/whale-trades/{job_id} and GET /api/v1/trader/{address}/export/status now return the completed storage object’s ETag as its actual text value for newly completed exports.Before this change, multipart completion could leave XML escapes such as " inside artifact.etag. Malformed completion metadata follows the existing reconciliation path. Historical artifacts and checksum values are unchanged.Backward compatible. The optional field keeps its meaning and shape.What to change: Treat new artifact.etag values as storage ETags; do not XML-decode them again. Check file integrity with the manifest’s SHA-256 values.Whale datasets add immutable downloads with checksums and replay continuation
POST /api/v1/datasets/whale-trades now queues an immutable cross-market snapshot for a finite past window. Poll its private job, download gzip NDJSON, or cancel it through the dataset resource.Before this change, cross-market history required pages of at most 100 rows; asynchronous exports covered one wallet at a time.- The manifest includes filters, the selection clock, a safe commit horizon, row and byte counts, content and compressed SHA-256, schema, source coverage, expiry, and an overlapping replay checkpoint.
- Continuation includes already committed arrivals after the requested window, plus later writers. Keep the same replay filters and deduplicate by whale-trade ID; this does not promise exactly-once delivery.
- Dataset requests share owner export quotas and lifecycle controls. Signed URLs expire within 1 hour and the artifact’s 24-hour retention deadline.
- Rows are detected whale alerts with exact decimal strings, not complete provider fills or enriched grades.
- Snapshot and replay use the same exact decimal size threshold. Pass the continuation’s
min_sizestring unchanged; the TypeScript replay operation accepts it without a floating-point conversion. Scientific spellings and tiny fractions use exact cent normalization.