Skip to main content

Apps & Brokers

Senticore is the venue: matching, settlement, custody proofs. An app is any client surface that brings users to that venue — the first-party frontends as well as a third-party broker with its own UI. Every app has the same identity model and the same rules; the first-party frontends sentico and sentipredict (the Sentirlo broker) are simply the first registered apps.

Operator-managed, not permissionless

Everything on this page is live, but apps are created, keyed and activated by the operator. There is no self-service registration, key rotation or app dashboard yet. Send your intended appId, display name and browser origins to [email protected]; see Onboarding.

Non-custodial by construction

A broker never holds user funds and needs no backend for trading. Users deposit into the vault under their own wallet, every balance is a leaf in the committed exit tree, and forced withdrawals work without the broker. A broker earns on the flow it brings, not on custody.

What is live​

CapabilityStatus
App registry with origins, server keys and status lifecycleLive
Identity binding on every channel: browser origin, server key, BSL / FIX / FIXP logonLive
Signed attribution (appId in every order payload, checked against the channel)Live
Surcharge (appFee) paid to the app's payout account on each fillLive
Per-app limits by status (order notional, daily notional, order-to-trade ratio, requests per second)Live
Hard suspension: open orders cancelled, private sessions closed, agents revocableLive
Private wallet reads: a user's positions, balances and history are visible only to that user's own sessionLive
Audit trail for every operator change to an appLive
Sandbox app registry with its own ids, origins and keysShips with the sandbox; see Sandbox apps and Status
Self-service registration, key rotation, app-level reporting dashboardNot available
Venue fee share paid to appsBy agreement only, operator-configured

The app record​

FieldMeaning
appIdStable identity, lowercase [a-z0-9_-], max 32 bytes. This is the string users sign as app:<appId> and the value carried in attributed order payloads. Choose it once.
displayNameWhat a wallet UI shows next to the signature prompt. Also accepted as the signed app: value.
statuspending → limited → active → suspended. pending and suspended apps cannot trade; limited trades under the onboarding limits, active under the full limits.
originsCanonical browser origins (https://trade.example.com). Requests from these origins are attributed to the app and pass CORS.
ownerWalletThe wallet that owns the app. Recorded for the audit trail today; becomes the self-service identity later.
payoutAccountThe Senticore account that receives the app's surcharge. Every appFee.payout in an order must equal this value byte for byte.
limitsOptional explicit limit profile. When absent, the defaults for the current status apply (see Limits).
embedAllowedReserved for the embeddable trade widget.

One identity on every channel​

ChannelHow the app is recognizedTypical user
BrowserRequest Origin ∈ registered originsBroker frontend where the user's own wallet signs
HTTP serverx-sentico-app-key: <keyId>.<secret> headerBroker backend relaying signed actions, scripts, SDKs
FIXApp key in the Logon message, tag 9500Institutional order routing
FIXPApp key in the Establish credentials fieldBinary institutional order routing
BSLApp key presented after the HELLO; the core binds the session to a capabilityMarket makers routing for an app
Signedapp:<appId> line inside every wallet-signed control-plane messageAgent authorization, session keys, account management, launchpad submit

The channels must agree. A request whose origin belongs to app A but whose key belongs to app B is rejected with APP_CHANNEL_MISMATCH. A message a user signed for app A cannot be relayed through app B. The signed domain: must be one of the app's registered origin hosts (or a parent domain of one). A request that presents no app at all is simply unattributed; a request that presents an invalid or revoked key is rejected with APP_KEY_INVALID rather than falling back to anonymous. Session-key registration (POST /api/v1/bsl/session-keys/register) follows the same rule: the request Origin and the signed app: line must belong to the same app (a mismatch answers 401), and the wallet-signed request must not carry machine-auth HMAC headers (403 otherwise).

Attributed orders​

Order payloads accept an optional appId:

{
"account": "0x…",
"nonce": 17,
"appId": "brokerx",
"ts": 1780000000000,
"action": { "SpotPlaceOrder": { "marketId": 7, "side": "Bid", "price": 998400, "qty": 1000 } }
}

appId is part of the signed canonical bytes: the user attests which app they traded through, and neither the relaying app nor the venue can rewrite it. It must name an active app and the same app the request arrived through, on every lane; otherwise the order is rejected with APP_ATTRIBUTION_REJECTED before it is sequenced. Payloads without appId are accepted and unattributed — every existing signing hash, order id and replay record is unchanged.

Spot market orders through an app

An app-attributed spot market order (isMarket: true) is rejected with APP_LIMIT_EXCEEDED, because it has no notional ceiling for the app's limit check. Send a price-bounded limit order with timeInForce: "ioc" instead; the first-party frontends already do this. Binary markets are unaffected because their price is bounded by 1 USDC.

Surcharge (appFee)​

An app may add its own fee on top of the venue fee. The surcharge is declared per order, signed by the user, and credited to the app's payout account inside the fill — no statement, no delay.

"appId": "brokerx",
"appFee": { "feePpm": 5000, "payout": "0x27798dbec1cc0f7e3531c51bab3bdb7315c5b32c" }
ProductCapHow it is charged
Spot5,000 ppm (50 bps) of notionalflat
Prediction20,000 ppm rate (2 %)rate × p × (1 − p); at p = 0.5 a 5,000 ppm rate is 12.5 bps of notional
Launchpad markets0no app surcharge; the deployer's fee applies

Rules: appFee requires appId; payout must equal the app's registered payoutAccount; a value above the cap is charged at the cap. The venue fee reported on the trade record excludes the surcharge, so tier and programme calculations never include it. The first-party frontends read appId and appFee from their runtime config; a broker frontend does the same.

Known gap

On prediction markets the surcharge credit currently appears in the payout account's balance but not yet as a separate app_fee line in the balance-events history. Spot fills already produce the line. A backend correction is scheduled.

Limits​

Limits are enforced at admission on every lane and keyed by the app, not the user. Defaults follow the app's status; the operator can set an explicit profile.

StatusPer orderPer UTC dayOrders per tradeRequests per second
limited1,000 USDC10,000 USDC10010
active1,000,000 USDC100,000,000 USDC10,00010,000

pending apps are registered but may not trade until the operator moves them to limited. The daily budget counts admitted notional, including orders later cancelled. A breach is rejected with APP_LIMIT_EXCEEDED and the detail names the limit.

Suspension​

Setting an app to suspended is a hard stop, applied in one transaction with the audit entry:

  1. New requests from its origins or keys are rejected with APP_NOT_ACTIVE; signed app: lines for it fail.
  2. All open orders of accounts trading through the app are cancelled by the sequencer, and scheduled algo/conditional orders are removed.
  3. Private WebSocket sessions of the app are closed; the operator may revoke every agent the app created.
  4. Status changes away from suspended are blocked until the cancellations have completed. Reactivation restores nothing.

Privacy​

Since September 2026 every wallet-scoped read — positions, balances, orders, fills, balance events, fee standing, statements — requires the read credentials of that wallet's own session and answers 401/403 otherwise. An app therefore sees exactly what each of its users sees in their session, and nothing about other apps or other users. Its own aggregated numbers come from the app reads under Reporting, which pseudonymise traders.

Revenue and reporting​

  • Surcharge: credited per fill to payoutAccount. Read it with that account's session via /api/v1/accounts/{account}/balance-events (spot) or the balance itself (prediction, see the known gap above).
  • Venue fee share: not automatic. Where an agreement exists, the operator configures it and the share is paid through the fee programme statements, readable with the payout wallet's session at /api/v1/fees/statements.
  • Volume: attributed volume is recorded per order in the venue's order history and reported through the app reads below.

Reporting​

Three private reads give an app its own numbers. They accept either the owner wallet's read credential (the same bearer or HMAC v2 session as /api/v1/accounts/{account}/*) or the app's own key channel (x-sentico-app-key, or a FIX/FIXP/BSL logon) resolved to the same appId; anything else answers 401, a foreign credential 403 FORBIDDEN, an unknown app 404 APP_NOT_FOUND. Responses carry Cache-Control: private, no-store and take no wallet selector.

  • GET /api/v1/apps/{appId} — the app record: display name, status, origins, owner and payout wallets, limit profile and key metadata. Key secrets and their hashes are never returned.
  • GET /api/v1/apps/{appId}/overview?range=24h|7d|30d|all — surcharge credits and broker fee-programme share (earned, paid, pending), attributed orders and fills with volume, active and new traders, a per-market breakdown, and the limit profile with today's admitted notional. range defaults to 7d; rejects is reported as unavailable in v1.
  • GET /api/v1/apps/{appId}/fills?range=&limit=&cursor= — attributed taker fills newest first (limit up to 500, opaque cursor), each with a stable per-app trader pseudonym instead of the account. Send Accept: text/csv for the same rows as CSV with a header line; the continuation cursor is then in the X-Next-Cursor response header.

Errors​

CodeHTTPMeaning
APP_KEY_INVALID401The x-sentico-app-key (or logon key) is unknown or revoked
APP_NOT_ACTIVE403The app exists but is pending or suspended
APP_CHANNEL_MISMATCH403Origin and key identify different apps
APP_ATTRIBUTION_REJECTED403Payload appId/appFee does not match the channel's app, or appFee.payout is not the registered payout account
APP_LIMIT_EXCEEDEDrejectOrder notional, daily budget, order-to-trade ratio, request budget, or an unbounded spot market order

Sandbox apps​

The sandbox (launching — see Status) has its own app registry. A sandbox app is how you exercise the whole broker path — origin binding, app:<appId> signatures, attributed orders, appFee, per-app limits, APP_CHANNEL_MISMATCH — before anything you build touches real collateral.

SandboxProduction
RegistrySeparate registry on the sandbox deploymentLive registry
OriginsYour sandbox origins, registered against a sandbox appIdYour production origins
Server keyIssued for the sandbox onlyIssued for production only
Signed chainId42161442161
Payout accountA sandbox account holding mock USDCYour real payout account
LimitsThe active profile, so throughput is representativeProfile by app status

Ask for a sandbox app the same way you ask for a production one (see Onboarding) and say that it is for the sandbox. Registration is not coupled: you can hold a sandbox app while your production application is still under review.

A sandbox app identity is not valid in production

A sandbox appId, its server key and every signature made under it exist only in the sandbox. The registries are separate rows in separate databases and the signed chainId differs, so a sandbox key presented to production is rejected as an invalid key and a sandbox-signed message is rejected on its chain binding. The reverse holds too. Keep the two configurations apart in your deployment; do not try to reuse one identity across environments.

Surcharges (appFee) in the sandbox are paid in mock USDC to a sandbox payout account. They exercise the accounting path; they are not revenue.

Onboarding​

  1. Send appId, display name, browser origins and, if you want a surcharge, the payout account to [email protected]. Say whether you want the app in the sandbox, in production, or both.
  2. The operator registers the app as pending, issues a server key if you run a backend or route through FIX/FIXP/BSL, shares the key once, and moves the app to limited.
  3. Point your frontend at the venue: users sign app:<appId> with your domain, orders carry appId (and appFee), your backend sends the key.
  4. Verify under the limited profile: a signed order lands in the venue's order history with your appId; a wrong payout is rejected.
  5. The operator sets the app active; from then on the active limits apply. No venue restart is involved at any step.

Rotate a server key by asking the operator for a new one and revoking the old one. Origin changes are operator changes too.

First-party clients​

sentico and sentipredict — the latter being the Sentirlo broker, which kept its original appId after the rename — are registered apps with their own origins and sign their own appId. Clients deployed before the registry signed the fixed name SentiPredict; that name stays accepted as an alias during the migration window and is attributed to the app the request arrived through. New integrations must sign their own appId.