How State Syncs
Writes land on chain first and reach the API on confirmation. What your app can observe at each step, and the guarantees you can build on.
Every write in this protocol happens in two parts: your app broadcasts a transaction to the chain, and the protocol API records the result once that transaction confirms. Reads are ordinary and immediate. Writes are not, and the gap between them is the thing to design around.
Close the flow on broadcast
A transaction succeeds when it broadcasts. That is the moment your UI should close the modal, clear the form, and tell the user their work is on its way. The protocol owns confirmation from there.
Do not poll the chain from your client. A client that waits for a block before dismissing a dialog is holding the user hostage to network latency it cannot control, and it duplicates work the protocol already does reliably. If you need to reflect the confirmed state, refetch through the SDK - the API is the read surface for confirmed state, and the chain is not.
What you can observe
Once the transaction confirms, the change shows up in ordinary reads:
| You broadcast | What changes when it confirms |
|---|---|
| A contract deploy | SmartProduct.status goes DRAFT to PUBLISHED, contractAddress is populated |
| A mint | A new ProductInstance appears with its instanceNumber and ownerAddress |
| A transfer | ProductInstance.ownerAddress updates; minterAddress does not, so provenance survives |
| A burn | burnedAt and burnTxId are set, and currentInstances decrements |
| A lock or settle (PREDICTION) | SmartProduct.status goes LOCKED, then SETTLED |
| A game action (GAME) | The match read (rtClient.games.getMatch) shows the new board, turn and status |
currentInstances counts confirmed mints only. A broadcast that has not yet confirmed is not in it.
Timing
Stacks blocks settle every few seconds, so most confirmations land quickly. Bitcoin-level finality arrives when Stacks anchors to a Bitcoin block, roughly every ten minutes. For UI purposes the first is the one that matters.
The permanent-storage copy of an edition trails slightly further behind. The off-chain record and a resolvable token URI both exist the moment the transaction confirms, so a collector sees their edition immediately; the archival upload completes shortly after.
Following a specific transaction
When you do need to know what became of one transaction - a status chip, a retry affordance, a receipt screen - register it and poll the protocol rather than the chain:
// txId comes from the broadcast itself: a commands.submit result, a privy.sign* response,
// or the wallet's own broadcast on the connected-wallet path
await rtClient.pendingTransactions.register({ txId });
const status = await rtClient.pendingTransactions.getStatus(txId);Privy publish and collect flows self-register server-side, so those need only the getStatus call. Bring-your-own-wallet flows register at broadcast time.
See rtClient.pendingTransactions for the full surface.
The guarantees you can build on
These hold regardless of how the protocol implements them, and they are what let you write a thin client:
- Confirmation is not lost. If a confirmation is delayed or a delivery is dropped, the protocol recovers it. Your app does not need a reconciliation path of its own.
- Nothing is applied twice. Every confirmation is idempotent on its transaction id, so a redelivered or replayed event converges on the same state rather than double-counting.
- Reorgs resolve themselves. A transaction reorganized out of the chain and re-mined lands the same final state. One that is never re-mined is walked back, so your reads do not keep an edition that no longer exists.
- Eventual, not immediate. The one thing you must design for is the gap. Show the user their work is in flight; do not block on the confirmation.
Failure
A broadcast that never confirms - dropped from the mempool, or rejected on inclusion - leaves the product in DRAFT. The protocol eventually flips it to FAILED. A FAILED product can be archived but cannot recover to PUBLISHED: the deployer creates a new draft and tries again.
See also
- On-chain Lifecycle - the deploy, mint, transfer and burn sequence
- Chainhooks - the event types the protocol observes on your behalf
- Status Machines - every status and its transitions
Chainhooks
Chainhooks are how the protocol observes confirmed on-chain transactions on your behalf, and the event types your app sees the results of.
Invites
Invites are first-class protocol objects that grant access, optionally assign a role, carry app grants plus starter STX, and can be locked to a verified identity.