How it worksIntroduction

How it works

From news event to tradable basket.You keep the licence, money, and customers.

Cascade builds news-driven baskets and related compute. You choose which modules we run; the rest stays on your stack.

Introduction

A customer who wants exposure to a news theme (for example rising oil prices) can take a single contract on a basket of related stocks, long some names and short others.

Cascade handles the steps from that order intent through to a booked, hedged position, for as many of those steps as you buy.

We never hold your customers' money. In any configuration.

Broker and Venue

Cascade uses two roles throughout the product:

You are the Broker. Your customers stay with you. The Venue is where the hedge is placed.

Broker

The licensed firm that faces retail customers, books contracts, holds money in trust, and transmits hedges.

Venue

Where the hedge is executed: an exchange, liquidity provider, or cover counterparty. The Broker holds the account there.

The five modules

Five separate products, not five phases of one product. A module never reaches inside another. That is why you can buy three and build two yourself.

  1. 01 Basket intelligence

    Live

    Maps a news theme to a basket of constituents with direction and weight.

    Consumes
    News / themes
    Produces
    A basket: constituents, direction, weight, disclosure
    If you turn it off
    You write your own baskets
  2. 02 Index and marks

    Live

    Publishes an index level and live marks for each basket.

    Consumes
    A basket + licensed market-data feed
    Produces
    A mark: basket, index level, timestamp
    If you turn it off
    You calculate your own index and license your own data feed
    • The index is a level, not a price. It starts at 1,000.
    • It is spread-free. Cascade publishes the raw index; you apply your own spread.
  3. 03 Exposure and netting

    Live

    Nets customer order intent into hedge deltas across the book.

    Consumes
    Basket, mark, order intent
    Produces
    Net exposure, hedge delta
    If you turn it off
    You do your own netting and hedge sizing
  4. 04 Risk compute

    Not built

    Computes valuation and advisory risk signals from marks and positions.

    Consumes
    Marks, positions
    Produces
    Valuation and advisory risk signals
    If you turn it off
    You do your own margin and loss-cut maths
    • Signals are advisory. Cascade computes; the Broker acts.
    • Not available on the live landing path today.
  5. 05 Ledger of record

    Not on sale

    Cascade holds the contract (full-service tier).

    Consumes
    Order intent, risk signals
    Produces
    Contract state
    If you turn it off
    You keep your own customer ledger (the normal case)
    • Not available in Japan. Do not treat as the default path.

Pick your configuration

The more licensed and built-out you are, the fewer modules you need.

If you hold the licence and run your own platform, take modules 1 to 3 (or 1 to 4) and keep your customer relationship entirely. If you have an app and no dealer platform, take all five, where the jurisdiction allows.

#You areYou takeWe doYou do
1A media company, or a Broker who just wants ideasM1BasketsEverything else
2A Broker who wants a branded index to quote againstM1–M2Baskets + indexSpread, ledger, margin, netting, hedging
3A licensed Broker with your own platformM1–M3Baskets, index, netting, hedge deltasLedger, margin, loss-cut, order transmission
4A Broker with a ledger but no derivatives risk mathsM1–M4The above + advisory risk signalsLedger, and you still enforce
5An app with no dealer platformM1–M5Everything up to the contract itselfThe app, and the licence

How money and risk flow

The customer relationship never leaves the Broker. Cascade computes; the Broker books and transmits.

Customer
Broker
Cascade
Venue
  1. 01

    Customer

    Taps Invest on a published basket in your app.

  2. 02

    Broker

    Quotes an indicative index, books the contract in your ledger, holds margin in your trust account.

  3. 03

    Broker

    Reports the order intent to Cascade.

  4. 04

    Cascade

    Decomposes into signed legs, nets across customers, emits hedge deltas.

  5. 05

    Broker

    Transmits cover orders to the Venue from your own OMS.

  6. 06

    Broker → Cascade

    Reports fills; Cascade updates cover held and streams fresh marks.

Getting started

  1. 01

    Choose Broker or Venue

    Create an organisation. Brokers launch indices; Venues route cover flow.

  2. 02

    Complete due diligence

    Licence checks and profile assignment decide which modules are enabled for your jurisdiction.

  3. 03

    Connect and go live

    Issue API keys, wire webhooks and SSE marks, connect your Venue, then publish your first basket.

API integration

Wire Cascade into your product from your Broker backend. Call Business with an API key; Business proxies allowlisted Engine routes and serves live SSE. Full request/response schemas live in Swagger.

Cascade holdsCascade never holds
A customerRef — an opaque string you chooseCustomer name, email, address or account number
Contracts, exposure and marks keyed on that stringCustomer money, balances or funding currency
Your configuration and entitlementsYour KYC records or suitability questionnaire answers

One currency per organisation

Your organisation has a single exposure currency, USD unless configured otherwise. Cascade converts nothing. You hold your customer's funding currency and convert on your own books at your own rate; that rate never reaches Cascade. A request naming a different currency is rejected with currency_mismatch, naming both — never coerced.

Start here

OpenAPI · Swagger UI

Broker Server OpenAPI is served on your Business host at /docs/tenant. Use your sandbox Business URL when exploring the API.

/docs/tenant

Auth

Authorization: Bearer csk_…

Keys: csk_test_ / csk_live_. Issue keys in the Broker console under API keys.

Bases

  • /v1/engine/*REST: catalog, index, trading, positions
  • /v1/stream/*SSE: live marks and event fan-out

API keys & scopes

Keys are issued from the Cascade dashboard. csk_test_… keys are available immediately; csk_live_… keys require an approved organisation — so you can build the entire integration before due diligence completes.

OperationEndpoint
Issue a keyPOST /v1/console/api-keys — environment, scopes, optional modules and expiresAt
List keysGET /v1/console/api-keys — metadata only
RotatePOST /v1/console/api-keys/{keyId}/rotate
RevokeDELETE /v1/console/api-keys/{keyId}
What you may requestGET /v1/console/api-keys/catalogue

The secret is shown once

Cascade stores a hash and a short display prefix. No endpoint will show you the key again — if you lose it, rotate.

Scopes are exact strings. There is no hierarchy and there are no wildcards — read does not imply baskets:read, and admin does not imply trade.

ScopeGrants
baskets:readCatalog browsing, event lists and detail, basket reads, dossiers
index:readIndex levels, index history, the live mark stream
readExposure reads, reconciliation breaks, the event stream
adminPublishing and lifecycle writes: publish, patch, close, resolve, unpublish, declare a subscription or spread policy, submit a reconciliation statement
tradeOrder submission (Modules 3 and 5)
config:readYour resolved configuration and clamp list
quotes:writeIndicative quoting, where enabled
positions:writeMargin preview and valuation (Module 4)
risk:readRisk signals and risk.* events (Module 4)

Separate keys keep a leak bounded and let you rotate one service without an outage across your platform.

Display

baskets:read index:read

Renders markets, charts and event pages.

Treasury

read

Watches exposure and runs reconciliation.

Authoring

admin baskets:read

The back-office job that publishes and resolves markets.

Ops

config:read

Compliance and operations tooling.

Modules & entitlements

Module 1 is always available. Modules 2–5 are enabled per organisation. Every request is checked in three stages, and each one fails differently.

StageChecksFailure
1 · AuthenticationValid, unrevoked key401 unauthorized
2 · ScopeThe key carries the scope this endpoint needs403 forbidden
3 · ModuleThe module that owns this endpoint is enabled for you403 module_not_enabled

Managing entitlements

WhatWhere
See what you holdGET /v1/console/org/configuration → modules
Request a modulePOST /v1/console/org/module-requests (Modules 2–5)
Track a requestGET /v1/console/org/module-requests/{ticketId}

Related codes

CodeMeaning
module_forbidden_by_profileYour organisation cannot hold this module, for jurisdiction or deployment reasons. Requesting it will not help.
profile_not_availableThe requested deployment profile is unknown, switched off, or wrong for your jurisdiction.

Branch on the code, not the status

forbidden means fix the key. module_not_enabled means enable the module. Surface the module field to whoever operates your integration. If the configuration read reports that values could not be retrieved, treat your entitlements as unknown, not empty.

How to integrate into your product

  1. 01

    Authenticate

    Call Business with your API key. Unknown /v1/engine/* paths return 404; only the allowlisted surface is open.

    Authorization: Bearer csk_test_xxxxxxxx
    Content-Type: application/json
  2. 02

    Load catalog into your product

    List published events and templates, then show constituents to customers.

    GET /v1/engine/events
    GET /v1/engine/events/{eventId}
    GET /v1/engine/catalog/templates
  3. 03

    Quote against the live index

    Read the current level (starts at 1,000) and subscribe to SSE marks. Apply your own spread in your app. Cascade is spread-free.

    GET /v1/engine/baskets/{basketId}/index
    GET /v1/stream/marks
  4. 04

    Record suitability, then trade

    A first buy is refused without a passing suitability row. Preview writes nothing; reserve holds capacity; commit opens the contract.

    PUT  /v1/engine/traders/{traderId}/suitability
    POST /v1/engine/buy/preview
    POST /v1/engine/buy/reserve
    POST /v1/engine/buy/commit
  5. 05

    Show positions and closes

    Read the book your customers hold, preview a close, then queue the sell.

    GET  /v1/engine/positions
    GET  /v1/engine/portfolio
    POST /v1/engine/sell/preview
    POST /v1/engine/sell
  6. 06

    Listen for hedge and lifecycle events

    Subscribe to the event stream for hedge-delta and trade lifecycle fan-out from Business.

    GET /v1/stream/events

API reference by module

Every allowlisted Broker Server route, with the same request and response detail as the Swagger UI. Click an endpoint to expand it. Scope hints must be present on your API key.

Module 1

Basket intelligence

Where markets come from: the licensed templates that define them, the events you publish from those templates, the baskets a customer buys, and the disclosure documents behind both. Always available — there is no module gate here, so a failure is the key or the request.

Module 2

Index and marks

The published level for each basket, its history, and a live feed. Requires the Module 2 grant and the index:read scope — baskets:read does not imply it, because basket intelligence and the index are separately licensed.

Module 3

Exposure and netting

Your firm's book: the running net per underlying and how much is covered. Exposure is not positions — positions are per-customer contracts, exposure is what has to be hedged. Nothing in this module moves a customer's money.

Events

Streaming

A live feed of the event catalogue for your organisation, spanning every module you hold.

Trading, positions & book

Modules 3 and 5 in the licensed sense — order lifecycle and the book a customer holds. Outside the Cascade API Reference's scope, so summarised here rather than expanded.

MethodPathSummaryScope
PUT/v1/engine/traders/{traderId}/suitabilityRecord suitabilitytrade
POST/v1/engine/buy/previewPreview a buytrade
POST/v1/engine/buy/reserveReserve a buytrade
POST/v1/engine/buy/commitCommit a buytrade
POST/v1/engine/pending-orders/{pendingOrderId}/cancelCancel a queued buytrade
POST/v1/engine/sell/previewPreview a selltrade
POST/v1/engine/sellQueue a selltrade
GET/v1/engine/positionsList positionsread
GET/v1/engine/portfolioPortfolio summaryread
GET/v1/engine/tradesList tradesread
GET/v1/engine/trades/{tradeId}/statusTrade statusread
GET/v1/stream/eventsLifecycle / hedge events (SSE)API key

Idempotency

Send an Idempotency-Key on every non-idempotent write.

  • Repeating a request with the same key and same payload returns the original result.
  • Reusing a key with a different payload is 409 conflict — never a silent overwrite.
  • Keys are scoped to your organisation.
  • Reuse one key across retries of the same logical request; generate a new one for a genuinely new request.

Some endpoints carry their own natural handle — publishKey on POST /v1/engine/events — and behave the same way.

Replay flags — treat every one as success

FlagEndpoint
replayedPOST /v1/engine/events
alreadyClosedPOST /v1/engine/events/{id}/close
alreadyStartedPOST /v1/engine/events/{id}/resolve
alreadyUnpublishedPOST /v1/engine/events/{id}/unpublish
statementsAlreadyOnRecordPOST /v1/engine/reconciliation/snapshot

Errors & retries

Every error uses the same envelope. code is a closed set — branch on it. message is for humans and may be reworded at any time.

FieldTypeDescription
codestringA closed set. Branch on this.
messagestringFor humans. May be reworded.
modulestring?Present only on module_not_enabled.
ruleIdstring?Present only on intake_halted. Stable, so you can key support tooling on it.
requestIdstringLog this on every failure — it is what makes a support request answerable.

load_shed and unavailable are not the same

Both are 503. load_shed means nothing is broken: your request was declined before it was attempted and the same call will succeed shortly — retry with jitter. unavailable means a dependency is down — retry with backoff, and alert.

You can always close, even when you cannot open

Under sustained load Cascade declines work in a fixed order: reporting and administrative endpoints first, opening next, and closing, exposure reads and the mark feed last. So never build a close path that depends on a reporting or administrative call — and if exposure reads or marks start failing, escalate rather than degrading quietly.

attempt (with Idempotency-Key)

  429                  -> wait the advertised delay, retry
  503 load_shed        -> jittered backoff, retry
                          (expected to succeed)
  503 unavailable, 500 -> jittered exponential backoff,
                          cap attempts, then alert
  other 4xx            -> do not retry; it will fail
                          identically

Rate limits & pagination

Requests are metered per organisation with a token bucket: capacity is your burst, refilled at your requests-per-second. Over the limit returns 429, with the retry delay in the body.

Read x-ratelimit-remaining on every response and throttle before you get there. Back off on 429; do not hot-loop.

StyleEndpointsHow
CursorGET /v1/engine/events, GET /v1/engine/baskets/{id}/index/history?limit=50&cursor=…. An empty page with no nextCursor means there is no more data. Never construct a cursor — pass back exactly what you were given.
OffsetGET /v1/engine/reconciliation/breaks?limit=50&offset=0; the response carries nextOffset.

Index history pages backwards

Its cursor walks towards from, oldest-last. That is how you find the start of a basket's series.

Conventions

  • Money fields are decimal strings, never JSON numbers.
  • customerRef / traderId are opaque. Cascade holds no customer identity.
  • Index values are levels starting at 1,000, not currency prices.
  • Buy preview always returns indicative: true and never a firm entry price.
  • POST /quotes is not on the Broker Server allowlist. You own the pricing layer.

Webhooks

Cascade publishes the same catalogue two ways: webhooks for anything your back office must not miss, and the event stream for anything a screen is watching. Most integrations use both — if you take only one, take webhooks, because they retry.

EventModuleExtra scope on the stream
basket.published1baskets:read
basket.updated1baskets:read
mark.published2index:read — on the webhook channel this carries status changes only; every level goes out on the stream
mark.impaired2Marks stopped being good enough to value a position against
mark.restored2Closes an outstanding mark.impaired
hedge.delta.ready3
hedge.fill.recorded3read
reconciliation.break3read
risk.margin_call4risk:read
risk.losscut_breach4risk:read
policy.clamp_applied—config:read
module.blocked—read

Delivery headers

HeaderDescription
X-Cascade-Signaturev1=<hex> — HMAC over the timestamp and the body.
X-Cascade-TimestampWhen the delivery was signed.
Content-Typeapplication/json.

Verify in this order

  1. Read the raw body bytes. Do not parse, re-serialise or canonicalise first — re-serialising can reorder a map, and you would then verify a signature over something that was never sent.
  2. Compute the HMAC over the timestamp and the body. The signature covers both, so a replay cannot be given a fresh timestamp.
  3. Compare in constant time.
  4. Reject a timestamp outside your tolerance window.
  5. Only then parse.

Delivery is at least once

Deduplicate on the event id and make handlers idempotent. Respond 2xx quickly and do the work asynchronously — a slow handler looks like a failure and earns a retry.

An event for a module you do not hold is not discarded

It waits. If you enable the module later, what was owed is delivered. And module.blocked is never gated on the module it reports — route it somewhere a human reads, because it is how you discover part of your integration is calling something you do not hold.

POST /cascade/webhook HTTP/1.1
Host: yourfirm.example.com
X-Cascade-Signature: v1=4a7f9c…
X-Cascade-Timestamp: 2026-09-23T21:05:00Z
Content-Type: application/json

{ "eventType": "reconciliation.break",
  "eventId": "9f30…",
  "occurredAt": "2026-09-23T21:05:00Z",
  "data": { "checkId": "e1f2…", "symbol": "MSFT",
            "severity": "high", "frozen": true } }
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody, headers, secret) {
  const ts  = headers["x-cascade-timestamp"];
  const sig = (headers["x-cascade-signature"] || "").replace(/^v1=/, "");

  if (Math.abs(Date.now() - Date.parse(ts)) > 5 * 60_000) return false;

  const expected = createHmac("sha256", secret)
    .update(ts).update(rawBody)
    .digest("hex");

  const a = Buffer.from(sig, "hex");
  const b = Buffer.from(expected, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Error codes

The complete set, with whether retrying is worth it.

StatusCodeMeaningRetry
400invalid_requestMalformed or missing fieldsNo
401unauthorizedMissing, invalid or revoked keyNo
403forbiddenValid key, wrong scopeNo — reissue the key
403module_not_enabledModule not enabled (see module)No — request the module
403module_forbidden_by_profileYour organisation cannot hold this moduleNo
403profile_not_availableDeployment profile unknown, off, or wrong for your jurisdictionNo
403tenant_restrictedRestricted for unpaid invoices. New positions blocked; closing still allowedNo — settle the invoice
404not_foundNo such resource for youNo
409conflictIdempotency key reused with a different payload, or a state conflictNo
409tenant_suspendedOrganisation suspendedNo
409exposure_cap_not_configuredNo exposure cap configuredNo — contact Cascade
409restricted_symbol_blockedSymbol hard-blockedNo
422unprocessable_entityUnderstood but semantically invalid — e.g. impossible market timingsNo
422exposure_cap_exceededOut of capacityYes, once headroom frees
422restricted_symbol_flaggedSymbol flagged for review at authoringNo
422spread_not_declaredQuote requested with no spread policyNo
422index_not_quotableNo usable index to quote againstLater
403trading_haltedTrading is haltedLater
—intake_haltedAuthoring stopped; see ruleIdLater
429—Too many requestsYes, after the advertised delay
500internal_errorA fault on our sideYes, with backoff
503unavailableA dependency is unavailableYes, with backoff
503load_shedDeclined under load, before being attemptedYes — the same call will succeed

Degrading well

SignalDo
indexStatus: degradedShow “price unavailable”. Never a number.
mark.impairedMark your valuations impaired and say so.
Configuration values unreadableTreat entitlements and cap as unknown, not empty.
module.blocked events appearingSomething is calling a module you do not hold — fix the caller or request the module.
Reconciliation reads degraded and new positions failingAssume a freeze; escalate.
exposure_cap_exceededOut of capacity — retry when headroom frees. Distinct from exposure_cap_not_configured, which is a setup gap that retrying will never fix.

Going-live checklist

What to confirm before you point a live key at production.

Correctness

  • All money parsed as decimals, never floats
  • All four indexStatus values handled; degraded never renders a number
  • pricedAt used as the price time; ts only as the series x-axis
  • seq gate per basketId; gaps treated as normal
  • Movement labelled with changeSince / changeWindowFull
  • History read against the response's from/to; bucketWidth compared against resolutionServed
  • Closed-market gaps not interpolated
  • Replay flags treated as success

Access

  • Separate keys per concern; none over-scoped
  • No key in any shipped client, log line or error report
  • module_not_enabled handled distinctly from forbidden
  • Live key issued only after approval

Events

  • Signature verified over raw bytes and timestamp, constant time
  • Handlers idempotent, deduplicating on event id
  • module.blocked and reconciliation.break routed to a human
  • Reconnect: Last-Event-ID on events, seq gate on marks

Operations

  • x-correlation-id logged on every call
  • Rate-limit headers observed; backoff on 429
  • load_shed retried; unavailable alerted
  • Nightly reconciliation scheduled, alerting on widening cumulativeDrift
  • Close path independent of reporting endpoints

Glossary

TermMeaning
TemplateA reusable market definition. Not tradable.
EventA published, tradable instance of a template, with a trading window.
OutcomeOne resolvable result of an event. One basket per outcome.
BasketOne outcome's tradable instrument. basketId is the key used by Modules 2 and 3.
ConstituentOne leg of a basket: symbol, weight, direction.
IndexA basket's declared base-1,000 level. Not a price and not executable.
MarkOne published index observation.
Exposure / bookYour net notional per underlying across all your markets.
coverHeldNotional the venue has actually covered for a symbol.
Exposure capThe ceiling on your open and reserved notional, set by Cascade.
NettingCombining your customers' offsetting instructions so only the residual reaches the venue. You set the mode from the dashboard — it is your firm's execution decision, so there is no Cascade-side write.
BreakA disagreement between your statement and Cascade's records.
FreezeA block raised by a break; cleared by Cascade.
customerRefYour opaque customer reference — the only customer-shaped field in the API.