Rising Tide Developers
SDKCore

rtClient.productInstances

SDK methods for querying and managing product instances - editions, tickets, bracket entries.

Thin facade for direct ProductInstance access. Most reads route through smartProducts.getInstances - this surface exists for callers that have an instance id but not the parent product id, and for the off-chain write surface (burn record, feature toggle).

Also exposed as rtClient.instances (alias).

Lookup

rtClient.productInstances.getById(productId, instanceId)

Single instance when both ids are known.

const { data: instance } = await rtClient.productInstances.getById(productId, instanceId);

rtClient.productInstances.findByInstanceNumber(productId, instanceNumber)

Lookup by the human-facing edition / ticket / entry number. Returns the matching instance, or null when no row matches.

const instance = await rtClient.productInstances.findByInstanceNumber(productId, 7);

rtClient.productInstances.findByContractAndInstanceNumber({ contractAddress, contractName, instanceNumber })

Single round-trip lookup when the caller has the on-chain contract identifier but not the substrate productId. Used by collect-flow surfaces that arrive with only the contract handle.

const { data: instance } = await rtClient.productInstances.findByContractAndInstanceNumber({
  contractAddress: "SP...",
  contractName: "haiku-001",
  instanceNumber: 1,
});

Owner-side listing

To list every instance owned by a wallet across all products and domains, use usersService:

const res = await rtClient.users.getUserInstances(stxAddress, {
  domain: "PAGE",
  status: "CONFIRMED",
  sortBy: "createdAt",
  sortOrder: "desc",
  page: 1,
  limit: 50,
});

See Users for full params.

Burn record

rtClient.productInstances.burn(productId, instanceId, { burnTxId })

Records the burn transaction id against an off-chain ProductInstance row. Does not sign or broadcast the on-chain burn - that runs via rtClient.privy.signBurnEdition() (Privy path) or Stacks Connect stx_callContract (BYOW path). Callers invoke burn() after the on-chain broadcast for optimistic UI updates ahead of the chainhook confirmation.

await rtClient.productInstances.burn(productId, instanceId, {
  burnTxId: "0xabc...",
});

The server-side productInstanceService.burn is idempotent: the chainhook handler edition-closed.ts calls the same method when the on-chain event confirms, so both paths are safe to run.

rtClient.productInstances.featureEntry(instanceId) / unfeatureEntry(instanceId)

Toggles isFeatured on a ProductInstance. Tournament owners (and operators with MANAGE_TOURNAMENTS) call these to surface specific bracket entries on the public gallery.

await rtClient.productInstances.featureEntry(instanceId);
await rtClient.productInstances.unfeatureEntry(instanceId);

Private note

rtClient.productInstances.stageNote({ txId, productId, note })

Stages the private note a purchaser leaves for the product owner, on a product that composes the private-note capability. Call it after broadcasting the mint, with the resulting txId: the instance does not exist until the chainhook observes the mint, so the note is keyed to the transaction and the server folds it into the instance when it does. One note per mint (a retry with the same txId overwrites), at most 256 characters, refused with 400 if the product does not compose private-note.

await rtClient.productInstances.stageNote({ txId, productId, note: "Step-free access please" });

Nothing about it reaches the chain. That is the point of the capability: an access need disclosed to one organizer must not become a permanent public record. The owner reads it back with smartProducts.getPrivateNote.

Re-saving

rtClient.productInstances.requestResave(privyToken, instanceId)

Asks the protocol to re-render an edition's saved copy (the render lands in the protocol's landing store; writing the new copy anywhere permanent is a Save As the owner runs from their session). Owner only (the product author for edition #1), refused with 409 on a locked or burned edition. Returns the freshly derived archival state - render that rather than flipping local state. Pass null for the token to send the one already set on the client (a connected-wallet session). See Storage.

Minting

Instance creation is triggered by on-chain transactions, not by an SDK method. Mint flow:

  1. Build the mint transaction with the wallet helper (rtClient.privy.signPublishEdition() or Stacks Connect stx_callContract).
  2. Sign and broadcast.
  3. The MINT Chainhook handler creates the ProductInstance row server-side on confirmation.

See How State Syncs for when a mint becomes visible.