Skip to main content

Market lifecycle in the sandbox

Not reachable yet

The sandbox is not deployed yet. The commands on this page run once it launches; Status announces the launch.

Placing an order is the easy part to rehearse. Watching a market resolve is the part of a prediction market that is hardest to integrate, because it is the part your code sees least: a market closes, an outcome is decided, positions settle, balances move, and your reconciliation either agrees with the venue or does not. In production that path fires once per market, on the venue's schedule, and you cannot rehearse it.

The Senticore sandbox compresses it. A binary option can be created with a five-minute cutoff and an outcome that is deterministic before it is created. A resolution dispute - submit, challenge, finalize - can be run end to end in about two minutes instead of the 48 hours the same state machine takes in production. The code paths are the production code paths. Only the clocks are shorter.

What this page assumes

You have a funded sandbox account (see Sandbox) and a client that can sign. Nothing on this page requires an API that does not exist in production.

The three shapes, and what each can do

ShapeResolutionWho can resolve itRehearsal time
Binary option (binary_option)oracle_median - a price barrier on BTC, ETH or SOLThe oracle, automatically. An operator can also force it.5 minutes
Prediction (prediction)economic_dispute - a bonded resolution caseNobody can force it. Only the case state machine finalizes it.~2 minutes
Spot (spot)noneNothing. Spot books never resolve.n/a

GET /api/v1/public/markets/{marketId} tells you which one you are looking at: instrumentType and resolutionPolicy are both on the catalog entry. Branch on resolutionPolicy, not on the symbol.

A market that resolves in five minutes

What the operator creates

Ask the sandbox operator for a short-dated binary option. They create one with a price barrier bound to the BTC, ETH or SOL reference profile, a cutoff N minutes out, and a barrier deliberately placed relative to live spot so the outcome is fixed in advance:

  • Barrier below spot - the very first oracle tick has median >= trigger, the barrier latches, and the market resolves YES, typically within seconds of creation.
  • Barrier far above spot - nothing ever latches, and the first tick at or after the cutoff resolves the market NO.

The trigger operator is always gte. Barrier placement is the only degree of freedom, and it is what makes the outcome deterministic rather than a bet on the next five minutes of price action.

What you see, in order

  1. Open. status is open on the catalog entry, the book quotes, you can trade it. Buy YES or NO, or mint a complete set with a Split action if you want both sides.
  2. Resolution. status flips to resolved_yes or resolved_no. The engine cancels every resting order on the market as part of resolving it, so your open orders come back as cancellations - handle that, it is the same in production.
  3. Settlement. The settlement worker walks the holders and credits each one automatically. You do not send anything. There is no claim step: a Claim action on the public API is rejected with resolved markets settle automatically.
  4. Balance. Your USDC balance moves, and a balance event is emitted on your account stream.

Watch it with three reads:

# lifecycle status
curl -s https://api.sentico-sandbox.xyz/api/v1/public/markets/17

# the oracle's view: the median it computed, whether the barrier has latched
curl -s https://api.sentico-sandbox.xyz/api/v1/public/markets/17/oracle

# your balance, before and after
curl -s https://api.sentico-sandbox.xyz/api/v1/accounts/0xYOURACCOUNT/bootstrap \
-H "authorization: Bearer $SANDBOX_TOKEN"

Or subscribe to your private account stream and watch the settlement credit arrive without polling.

The one honest caveat

A binary market resolves when an oracle tick reaches the venue. The sandbox runs its own tick source; if that source is down, a market sits open past its cutoff instead of resolving. That is visible - status stays open, expiryTsMs is in the past - and it is the single failure mode worth building a timeout around in your test harness. Do not assume "cutoff passed" implies "resolved". Read the status.

A dispute you can rehearse in two minutes

A prediction market resolves through an economic dispute: somebody proposes an outcome and backs it with a bond, anyone else may challenge it with a bond of their own, and the venue finalizes. In production the submit and challenge windows are 24 hours each. In the sandbox they are set per market, and the rehearsal markets use 60 seconds each.

Why an operator cannot just resolve it for you

They cannot, and this is deliberate. The admin lifecycle route refuses an economic-dispute market with HTTP 409:

economic-dispute markets must be finalized through a resolution case

The admin finalize route is not a back door either: it only accepts a case that is already fully funded and in status challenged. There is no operator button that skips the state machine, in the sandbox or in production.

That refusal is the feature. The dispute state machine is exactly what you need to integrate against, so the sandbox compresses the timers rather than bypassing them.

The flow

Step 1 - see the terms before you commit.

GET /api/v1/protocol/markets/{marketId}/resolution-dashboard

The simulator block tells you the bond, the fee, the challenge window and whether the market is currently eligible for a case:

FieldMeaning
bondUsdcWhat submitting a case costs you, in USDC micros. 1% of the market's outstanding shares, clamped between a floor and a ceiling.
resolutionFeeUsdc1.5% of the same snapshot.
challengeWindowMsHow long the case is challengeable once your bond is applied.
submitDeadlineMs, fallbackAfterMsWhen the submit window closes and the fallback path takes over.
eligible, reasonWhether a case can be submitted right now, and why not if it cannot.

The market must be open for a case to be submitted. A closed, halted or already-disputed market returns eligible: false with the reason spelled out.

Step 2 - hash the payload you are about to sign.

POST /api/v1/protocol/markets/{marketId}/resolution-payload-hash
{ "outcome": "YES", "claim": "...", "artifacts": [] }

Returns payloadHash. Signing the hash rather than the free text is what keeps your evidence and artifacts bound to the signature.

Step 3 - submit the case.

POST /api/v1/protocol/markets/{marketId}/resolution-cases
{
"walletAuth": { "domain": "...", "appName": "...", "chainId": 421614,
"walletAddress": "0x...", "nonce": "...", "issuedAt": 0,
"expiresAt": 0, "signature": "0x..." },
"outcome": "YES",
"claim": "...",
"evidence": { "summary": "...", "primarySourceUrl": "..." },
"artifacts": []
}

The response carries caseId, status, bondUsdc, escrowAccount, challengeDeadlineMs and fallbackAfterMs. Two outcomes are normal:

  • HTTP 200 with status: "pending_challenge" - your bond was applied synchronously and the challenge window is already running. challengeDeadlineMs is set.
  • HTTP 202 with status: "pending_bond" - the bond funding is in flight. Poll the dashboard; it becomes pending_challenge once the bond lands.

Submitting a case halts the market. That is intentional: trading stops while an outcome is being decided. Your open orders on that market are cancelled.

Step 4 - (optional) challenge it. Same shape, one level down:

POST /api/v1/resolution-cases/{caseId}/challenge-payload-hash
POST /api/v1/resolution-cases/{caseId}/challenges

A challenge is only accepted while the case is in pending_challenge, and it costs a bond of its own. A challenged case stops auto-finalizing and waits for the venue to adjudicate.

Step 5 - watch it finalize. If nobody challenges inside the challenge window, the auto-finalizer picks the case up, refunds the bond and settles the market to the proposed outcome. From there it behaves exactly like the binary case: positions settle automatically, and no claim is needed.

The case states you will see

statusWhat it means
pending_bondCase created, bond funding in flight.
bond_failedBond funding was rejected - usually an insufficient balance. Call the faucet and start over.
pending_challengeBond applied. The clock in challengeDeadlineMs is running.
auto_finalizingNobody challenged; the finalizer has claimed the case.
challenge_bond_pendingA challenge was filed and its bond is in flight.
challengedChallenge funded. The venue now decides.
admin_finalizingThe venue has claimed the challenged case for adjudication.
fallback_pendingThe submit window expired with no case; the fallback path owns it.
finalizedDone. The market is resolved and settling.

A test harness should drive pending_challenge to finalized for the no-challenge path, and pending_challenge to challenged to finalized for the challenged path. Both fit inside three minutes on a 60-second-window sandbox market.

Timing, concretely

StepSandboxProduction
Submit a caseimmediateimmediate
Challenge window60 seconds24 hours
Submit window (fallback)60 seconds24 hours
Auto-finalize pollabout 1 secondabout 1 second
Settlement after resolveautomatic, one worker intervalautomatic, one worker interval

What an operator must do for you, and how to ask

Market creation is an operator action in the sandbox, exactly as it is in production. There is no public "create market" endpoint, in either environment, and the sandbox does not invent one - that would be a sandbox-only API, and a sandbox-only API certifies nothing.

Ask through the normal sandbox support channel, and give the operator these fields. Anything you leave out, they will pick a default for.

For a short-dated binary option:

Tell themExampleWhy
AssetBTC, ETH or SOLThese are the only three reference profiles that exist.
Time to cutoff5 minutesHow long you want to trade it before it resolves.
Desired outcomeYES or NOThey place the barrier to produce it deterministically.
Quote assetUSDCThe default; say so if you need otherwise.

For a dispute rehearsal market:

Tell themExampleWhy
Submit window60000 msPer-market, overrides the venue default.
Challenge window60000 msPer-market, overrides the venue default.
Question textfree textAppears in the catalog entry.

For a spot book: the base and quote asset ids, and the tick and lot size you want to test against. Note that a base asset that is not already registered has to be registered first - that is a separate operator action.

A standing pool of short-dated binary markets is kept in flight for exactly this reason, so in most cases you do not have to ask at all: enumerate GET /api/v1/public/markets, filter for instrumentType: "binary_option" with status: "open" and an expiryTsMs a few minutes out, and trade the one you find. Do not hardcode market ids for these - they are created and retired continuously.

Published caps

So you can plan a test suite around them rather than discovering them as errors:

CapValue
Short-dated binary markets kept in flight3
Their cutoffs8 to 16 minutes out, staggered so they do not all expire together
New rolling markets created per dayat most 96
Markets created on requestby arrangement, not self-service

What is NOT possible

The complete list. If something is not on it, it works the way production works.

You cannot create a market yourself. No public endpoint creates one, in the sandbox or in production. Ask the operator.

An operator cannot force-resolve a prediction market. The lifecycle route refuses with economic-dispute markets must be finalized through a resolution case, and the admin finalize route only accepts an already-challenged, fully funded case. Use the dispute flow.

A binary option cannot be written on an arbitrary asset. The Core accepts exactly three price-barrier source profiles - BTC, ETH and SOL, each against the same three reference venues. There is no way to bind a binary market to anything else without adding a new profile to the venue itself.

A binary option's barrier parameters are fixed. Poll interval, confirm samples, minimum healthy sources, the gte operator and the degraded policy are pinned by the Core. Only the trigger price and the cutoff vary. You cannot request "resolve NO if price falls below X" - you request a barrier placement that produces the outcome you want.

A spot market never resolves. Attempting to resolve one is rejected by the engine with spot market cannot be resolved as YES/NO outcome market.

A resolved market cannot be re-resolved to a different outcome. Re-resolving to the same outcome is a no-op; a different outcome is refused with market already resolved as X; refusing to overwrite with Y.

There is no claim step and no way to add one. Resolved markets settle automatically, and a Claim action on the public API is rejected. If your production integration has a claim step, it is doing something the venue does not ask for.

There are no withdrawals. The sandbox is fully off-chain. WithdrawRequest and WithdrawAssetRequest are rejected on every lane. Settlement credits your sandbox ledger balance, and that is where they stay.

Sandbox marks are not prices. Resolution outcomes on rehearsal markets are chosen by construction. Nothing about a sandbox market's price, depth or outcome says anything about the real world, and the reference prices that drive the barriers are read from public venue endpoints for realism only.

Building a test for this

A reasonable end-to-end resolution test looks like this:

  1. Call the faucet, confirm the balance.
  2. Find or request a binary market with a known outcome and a short cutoff.
  3. Take a position. Record your balance and the market's yesTotalShares / noTotalShares.
  4. Poll GET /api/v1/public/markets/{id} until status is terminal, with a timeout - a market that has not resolved past its cutoff is a venue problem, not a test failure to retry forever.
  5. Poll your balance until the settlement credit lands, again with a timeout.
  6. Assert that the credit equals your winning position and that your losing position went to zero.

For the dispute path, substitute steps 2 to 4 with: request a rehearsal market with 60-second windows, submit a case, optionally challenge it from a second funded account, and poll the resolution dashboard until the case reads finalized.

Both tests are worth running against the sandbox on every release of your client. Neither can be run against production at all.