Rising Tide Developers
API

Contracts and Signing

Generate Clarity source for a product type, serve token URIs, and server-sign transactions for custodial users.

Two different things live here: generating contract source, and signing transactions on behalf of a custodial user.

POST /api/contracts/generate returns Clarity source and never deploys anything: your client broadcasts it, or, for a hosted account, hands it to deploy-contract. The /api/privy/sign/* routes are the custodial path. The server holds the user's embedded wallet, so it signs and broadcasts on the user's behalf, the deploy included; on these routes the protocol wallet is never the signer. A bring-your-own-wallet user signs the same actions in their own wallet instead, using the SDK's transaction builders.

Token URI routes are keyed on (address, name), never a slug. That pair is baked into an immutable Clarity constant at deploy time, so a slug rename must never be able to break metadata resolution.

Generating source

MethodPathAuthPurpose
POST/api/contracts/generateapp key + app accessGenerate Clarity source. productType is one of haiku, tanka, basicpage, openletter, bracket, ticket or game-lobby. With mode: "deploy" and a smartProductId the split is rewritten from the product's frozen contributions, a client-supplied creators[] is refused, and the code is pinned for the deploy check. mode: "preview" writes and pins nothing. Limited to 30 requests a minute per IP.
POST/api/contracts/template/generateapp key + user, entitlementGenerate from a template without a draft product. For tooling. Needs MANAGE_CONTRACTS.
GET/api/templates/slotsapp key + user, entitlementThe slots a template declares. Use it to check a composition fits before generating. Needs CREATE_ASSETS.

Token URIs and metadata

The token routes are public: a wallet or marketplace reads them with no key, because the address and contract name are baked into the deployed contract.

MethodPathAuthPurpose
GET/api/contracts/{address}/{name}/nft-asset-classapp keyThe NFT asset class for a deployed contract.
GET/api/contracts/{address}/{name}/tokens/{index}publicToken URI metadata for one edition. Keyed on (address, name), never a slug.
GET/api/contracts/{address}/{name}/tokens/{index}/thumbnailpublicRendered thumbnail for one edition.
GET/api/contracts/writing/{deployer}/{name}/tokens/{index}publicToken URI for a writing-family contract.
GET/api/contracts/writing/{deployer}/{name}/tokens/{index}/thumbnailpublicThumbnail for a writing-family edition.

Server signing for custodial users

These routes sign for a hosted account. The session check is Privy's, so a bring-your-own-wallet token does not authenticate here: a wallet user signs the same actions with the SDK's transaction builders. Each request is also throttled per user. Responses are { "success": true, "txId": "0x..." } (the deploy route adds its fee estimate) or { "success": false, "error": "...", "details": "..." }, not the usual envelope. A failed entitlement or access check is the ordinary 403 envelope.

MethodPathAuthPurpose
POST/api/privy/create-walletapp key + userCreate an embedded wallet for a new user.
POST/api/privy/sign/deploy-contractapp key + user, entitlement, app accessDeploy a generated contract. Needs the CREATE_* entitlement of the draft's domain: CREATE_PAGES, CREATE_EVENTS, CREATE_BRACKETS or CREATE_GAMES. Refuses code that did not come from generation.
POST/api/privy/sign/publish-editionapp key + user, entitlement, app accessCollect an edition. Needs COLLECT_EDITIONS.
POST/api/privy/sign/publish-feedback-editionapp key + user, entitlement, app accessCollect an edition and leave a note, on a product composing feedback.
POST/api/privy/sign/publish-open-letter-editionapp key + user, entitlement, app accessCo-sign an open letter, with sentiment.
POST/api/privy/sign/transfer-editionapp key + user, entitlementTransfer an edition. Needs COLLECT_EDITIONS. No app access is required: ownership never expires.
POST/api/privy/sign/burn-editionapp key + user, entitlementBurn an edition. Needs COLLECT_EDITIONS. No app access is required.

Domain and capability actions

MethodPathAuthPurpose
POST/api/privy/sign/make-reservationapp key + user, entitlement, app accessReserve a ticket. Needs RSVP_EVENTS.
POST/api/privy/sign/validate-reservationapp key + user, entitlement, app accessValidate a ticket at the door. Needs CREATE_EVENTS.
POST/api/privy/sign/submit-bracketapp key + user, entitlement, app accessSubmit a bracket entry. Needs SUBMIT_BRACKETS.
POST/api/privy/sign/bracket-actionapp key + user, app accessOne tournament action: lock, unlock, enter-results, clear-result, play-in-result, clear-play-in, finalize-scores, claim-prize or withdraw-pool. Owner actions also need CREATE_BRACKETS and the deploying address; scoring and claiming are open to anyone signed in with access.
POST/api/privy/sign/game-actionapp key + user, app accessOne game-lobby action: join, leave, start, move, claim, claim-draw or use-bomb. See Games.
POST/api/privy/sign/set-detached-uriapp key + userPin an edition's token URI to a permanent URL. Owner-gated.
POST/api/privy/sign/lock-detachedapp key + userFreeze that pin forever. Owner-gated and irreversible.
POST/api/privy/sign/set-max-supplyapp key + user, entitlement, app accessChange the supply cap, where the supply constraint allows it. Needs CREATE_PAGES.
POST/api/privy/sign/set-product-infoapp key + user, entitlement, app accessUpdate the product-info attribute. Needs CREATE_PAGES.
POST/api/privy/sign/faucet-stxapp key + user, app accessTestnet gas top-up, capped per person per day. Testnet only.
POST/api/privy/sign/faucet-sbtcapp key + user, app accessTestnet sBTC top-up, capped per person per day, and refused on mainnet. Closed to everyone but operators wherever earning is meant to be the only source.

The delegate wallet

A person's delegate wallet is a second wallet, theirs, that an assistant they connect can spend from. It collects for them through the rt-delegates registry: what it pays for is minted to the person. The routes and the SDK name it delegateWallet. Every route here runs on the person's own sign-in, and the session's wallet must be their main wallet. A request carrying an assistant's grant is refused. Money only ever moves between the person's two wallets, so no route takes a recipient.

MethodPathAuthPurpose
GET/api/privy/delegate-walletapp key + userThe delegate wallet's address, where it stands in the registry (CREATED, NAMED or RESIGNED), and its two balances, sats and ustx, each a decimal string, or null when it could not be read. Beside it, wallet: the most the main wallet can move in (maxSats, maxUstx, after the network fee feeUstx), null where a balance could not be read. { "delegate-wallet": null } when there is none yet.
POST/api/privy/sign/open-delegate-walletapp key + userMake the delegate wallet if there is none, and have the main wallet name it in the registry. Signs nothing for a delegate wallet already named.
POST/api/privy/sign/fund-delegate-walletapp key + userSend amountUstx (a positive whole number, as a number or a decimal string) from the main wallet to the delegate wallet, for the network fees its spends pay. Refused before signing, with 409 and code: "OVER_LIMIT", when it is more than maxUstx; 503 when the balance cannot be read.
POST/api/privy/sign/fund-delegate-wallet-sbtcapp key + userSend amountSats from the main wallet to the delegate wallet, for it to spend. Refused before signing with 409: OVER_LIMIT over what the wallet holds, NO_FEE when it cannot pay the fee in STX; 503 when a balance cannot be read.
POST/api/privy/sign/empty-delegate-walletapp key + userEverything in the delegate wallet back to the main wallet: the sats, then the STX left after both network fees. Refused when a balance cannot be read.
POST/api/privy/sign/resign-delegate-walletapp key + userEnd the delegate wallet's standing in the registry. The delegate wallet signs when it holds the fee, and the main wallet otherwise. Its balance stays put.

Connected assistants

A person connects an assistant, such as Claude, from the assistant itself: it sends them to the hub's /connect page, which signs them in and completes the assistant's authorization. The assistant never holds a Rising Tide key. It calls the Rising Tide guide (rt-mcp), and the guide calls the routes below for it.

These run on the person's own sign-in. A request carrying an assistant is refused, so an assistant can neither connect nor disconnect itself, change what it may spend, or confirm through the person's link.

MethodPathAuthPurpose
POST/api/assistants/connectapp key + userBody { externalAuthId }, the code the authorization server put on the /connect link. Completes the authorization as this person and returns redirectUri, where to send them next. 403 for a signed-in person who has not joined; 400 when the link has expired.
GET/api/assistantsapp key + userThe person's assistants: id, name, status (ACTIVE or REVOKED), spendEnabled, appIds, dailySats, perTapSats, dailyActions, spentSatsToday, connectedAt and revokedAt. Also apps, the apps the person may let an assistant spend in (the catalog's, where they may act themselves), and limits, the most a day or a tap may be set to.
POST/api/assistants/revokeapp key + userBody { id }. Disconnects one assistant: its next request is refused, and it comes back only when the person connects it again, with spending off until they turn it on.
POST/api/assistants/settingsapp key + userBody { id, spendEnabled?, appIds?, dailySats?, perTapSats? }. What one assistant may spend: whether at all, in which apps, and how many sats a day and a tap. 400 for an app the person cannot use themselves, or an amount outside the limits.
GET/api/assistants/quoteapp key + userOne quote, named by ?id=, for the confirm link: what it buys, the person's own words for a reply or a signature exactly as they will be written, the price in sats, who gets paid (names, no figures), and what the assistant has left today. 404 for anyone but the person it would spend for.
POST/api/assistants/quote/confirmapp key + userBody { id }. The person confirms a quote from the link, for an assistant that draws no card. The same rule as the guide's tap, below. Returns txId.

The rest answer the guide only. They need its trusted-caller key and an x-rt-assistant header it signs with a secret it shares with rt-api, naming the person, the assistant and the path, within a minute. The first such call for a person and an assistant creates the grant, with spending off.

MethodPathAuthPurpose
GET/api/assistants/me/statusguideWhether this assistant's grant is open: name and spendEnabled, or 403 when the person disconnected it.
GET/api/assistants/me/inboxguideThe person's notification feed, across their apps or one (?appId=). Reading it marks nothing read.
GET/api/assistants/me/delegate-walletguideThe delegate wallet's two balances (null when unread), and what this assistant may still spend today under its grant.
GET/api/assistants/me/matchesguideThe person's games: a seat held at a table whose match has not started (status: 'SEATED', with canStart once the table is full), matches still being played and whose turn it is, finished matches with a pot or a drawn share still theirs (canClaim), and those that ended in the last week.
GET/api/assistants/me/logbookguideWhat the person made, what they keep, and what they have earned and spent in all, each currency in its own field.
POST/api/assistants/me/quotesguideBody { action: 'keep' | 'reply' | 'sign' | 'join', productId, note?, rating? }. A quote to keep a copy of a page, leave a reply on a bottle (note, required), sign a letter (rating 1-10, note optional) or take a seat at a game table (productId is the lobby, priced from its buy-in) from the person's delegate wallet: the action is the one the page's deployed mint takes; priced from the page in sats; who gets paid named from the page's frozen split; the person's words checked against the contract (length, characters) and returned as words exactly as they will be written, never rewritten; the budget checked without reserving. Single use, good for five minutes. 400 or 409 with words for the person when it cannot be quoted (spending off, the app not chosen, the delegate wallet not open or short, a page made before delegate wallets, the page taking another action, an author signing their own letter, words the contract cannot hold).
POST/api/assistants/me/spendsguideBody { quoteId }. The person tapped the quote card. In one transaction holding the grant: the quote is this grant's, unused and unexpired; spending is on and the app chosen; the price fits one tap, the day's sats and the day's actions. Then the delegate wallet signs the mint with the quote's words, the copy, reply, signature or seat owned by the person and the delegate wallet's sats pinned to the quoted price, and it returns action and txId at broadcast.
POST/api/assistants/me/playsguideBody { lobbyId, action: 'start' | 'move' | 'claim', input?, match? }. The person tapped the board: start the match at a full table they sit at, make a move (input, a column or a square), or claim a won pot or a drawn share (match). Checked against the table before anything is signed (a seat, the turn, room on the board, the winner), so an illegal move is refused in words rather than aborting on chain for a fee. No sats move from the delegate wallet; each tap counts against the day's actions and costs a network fee in STX. The pot goes to the person's main wallet. Returns action, match and txId at broadcast.

A spend that never broadcasts gives its price back. Once broadcast it follows its transaction: it holds its price while the chain has not answered and once it lands. One that aborts on chain gives its price back but still counts as one of the day's actions, because its network fee was paid. One that is never mined gives its price back, and holds it again if the chain later shows it mined.