Market lifecycle in the sandbox
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.
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
| Shape | Resolution | Who can resolve it | Rehearsal time |
|---|---|---|---|
Binary option (binary_option) | oracle_median - a price barrier on BTC, ETH or SOL | The oracle, automatically. An operator can also force it. | 5 minutes |
Prediction (prediction) | economic_dispute - a bonded resolution case | Nobody can force it. Only the case state machine finalizes it. | ~2 minutes |
Spot (spot) | none | Nothing. 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
- Open.
statusisopenon the catalog entry, the book quotes, you can trade it. Buy YES or NO, or mint a complete set with aSplitaction if you want both sides. - Resolution.
statusflips toresolved_yesorresolved_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. - Settlement. The settlement worker walks the holders and credits each one
automatically. You do not send anything. There is no claim step: a
Claimaction on the public API is rejected withresolved markets settle automatically. - 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:
| Field | Meaning |
|---|---|
bondUsdc | What submitting a case costs you, in USDC micros. 1% of the market's outstanding shares, clamped between a floor and a ceiling. |
resolutionFeeUsdc | 1.5% of the same snapshot. |
challengeWindowMs | How long the case is challengeable once your bond is applied. |
submitDeadlineMs, fallbackAfterMs | When the submit window closes and the fallback path takes over. |
eligible, reason | Whether 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.challengeDeadlineMsis set. - HTTP 202 with
status: "pending_bond"- the bond funding is in flight. Poll the dashboard; it becomespending_challengeonce 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
status | What it means |
|---|---|
pending_bond | Case created, bond funding in flight. |
bond_failed | Bond funding was rejected - usually an insufficient balance. Call the faucet and start over. |
pending_challenge | Bond applied. The clock in challengeDeadlineMs is running. |
auto_finalizing | Nobody challenged; the finalizer has claimed the case. |
challenge_bond_pending | A challenge was filed and its bond is in flight. |
challenged | Challenge funded. The venue now decides. |
admin_finalizing | The venue has claimed the challenged case for adjudication. |
fallback_pending | The submit window expired with no case; the fallback path owns it. |
finalized | Done. 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
| Step | Sandbox | Production |
|---|---|---|
| Submit a case | immediate | immediate |
| Challenge window | 60 seconds | 24 hours |
| Submit window (fallback) | 60 seconds | 24 hours |
| Auto-finalize poll | about 1 second | about 1 second |
| Settlement after resolve | automatic, one worker interval | automatic, 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 them | Example | Why |
|---|---|---|
| Asset | BTC, ETH or SOL | These are the only three reference profiles that exist. |
| Time to cutoff | 5 minutes | How long you want to trade it before it resolves. |
| Desired outcome | YES or NO | They place the barrier to produce it deterministically. |
| Quote asset | USDC | The default; say so if you need otherwise. |
For a dispute rehearsal market:
| Tell them | Example | Why |
|---|---|---|
| Submit window | 60000 ms | Per-market, overrides the venue default. |
| Challenge window | 60000 ms | Per-market, overrides the venue default. |
| Question text | free text | Appears 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:
| Cap | Value |
|---|---|
| Short-dated binary markets kept in flight | 3 |
| Their cutoffs | 8 to 16 minutes out, staggered so they do not all expire together |
| New rolling markets created per day | at most 96 |
| Markets created on request | by 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:
- Call the faucet, confirm the balance.
- Find or request a binary market with a known outcome and a short cutoff.
- Take a position. Record your balance and the market's
yesTotalShares/noTotalShares. - Poll
GET /api/v1/public/markets/{id}untilstatusis terminal, with a timeout - a market that has not resolved past its cutoff is a venue problem, not a test failure to retry forever. - Poll your balance until the settlement credit lands, again with a timeout.
- 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.