Changelog
Release notes for the SDK and CLI.
These notes come from the package changelogs in this documentation revision. Compare them with the versions in domino doctor --json, then read the entries between your installed version and the release you plan to use. Follow the upgrade guidance. Versions like 1.0.0-next.N are the v1 prereleases published before 1.0.0, where breaking changes were allowed between prereleases.
Some stable versions in these notes never reached npm. Each is marked Not published to npm and names the first published version that carries its changes; install that one instead. A documentation preview may also include versions not yet published. Confirm that the target release is available on npm before upgrading.
SDK
1.66.1
No changes in this release.
1.66.0
Minor Changes
-
165ca99: Harder sign-in.
domino logincodes now expire after 15 minutes, not an hour. Console's approval page shows when the request was made, the country of the IP it came from and the CLI's version and OS, and warns to approve only a code from your own terminal. The CLI sends that description as itsUser-Agent. One client network (an IPv4 address, or an IPv6 /48) holds at most 20 pending sign-ins.- Email sign-in codes name the app. The email says which app and host the code is for, and to enter it only there. Only the latest code for an address works: an earlier one answers
code-expired. Ten wrong codes for an address on a project in an hour, or twenty in a day, refuseemailVerifywithrate-limitedandRetry-Afteruntil the window ends, whatever new codes it gets. An address gets at most ten codes an hour from every app together; past that,emailStartanswersrate-limited. - Plugin OAuth connections finish in Console, as the person who started them (experimental). The vendor's callback no longer connects by itself: it sends the browser to Console with a one-time
oauthCode, and the newmanagement.connections.completeAuthorization(plugin, { code })redeems it within five minutes, for the same person, project and environment only (403scope-mismatchfor anyone else, 409oauth-flow-expiredfor a spent or expired code). WithoutreturnTo, the browser returns to Console's Integrations page for the project.authorizenow refuses automation, service and hosted app keys with 403credential-not-allowed. New experimental exports:connectionAuthorizationCompleteInputSchemaandConnectionAuthorizationCompleteInput.
1.65.0
Minor Changes
- 9a37fe5: Clear test data in hosted app storage.
domino storage clear sql DB --table tickets(or--all-tables --keep settings) empties SQL tables and keeps their schema andd1_migrations;domino storage clear kv PHOTOS --prefix photo:(or--all-keys) deletes KV keys.--dry-runcounts first, and without--yesthe command asks in a terminal and refuses unattended. The management API has the same operation asPOST /management/v1/storage/{kind}/{name}/clear, andcreateManagementClientasstorage.clear(kind, name, input). It works only in the test environment: live refuses withtest-environment-required. It needspublish, and each clear is audited. A KV clear deletes at mostlimits.apps.storageClearKeys(10,000) keys a call and answerscomplete: falsewhile keys remain; the CLI repeats until none do. Test member reset, purge and erase still leave app storage alone.
1.64.0
Minor Changes
-
0a4d580: Domino now deletes the evidence members submit (photos, text, links and quiz answers) 90 days after the attempt is decided, in test and live, unless you change it. Nothing was deleted when this shipped: evidence that already existed is first deleted 90 days after the change reached Domino, on 18 January 2027 for projects that existed on 20 October 2026, and 90 days after creation for newer projects. Attempts waiting for a decision keep their evidence. Each project's date shows in Console's project settings and in
noticeEndsAt.To keep evidence longer, or until you delete it, change the period in Console's project settings, with
management.attempts.setEvidenceRetention({ evidenceRetention: { days } })(1 to 3650 days) or{ evidenceRetention: "keep" }, or withdomino attempts set-evidence-retention --days Nor--keep. Changing it needspublish; shortening it, or switching from keep to days, also needs an organization admin or a service key.management.attempts.evidenceRetention()anddomino attempts evidence-retentionread it. Both methods are experimental. Each change is recorded in the project's access history, and each deletion pass as anevidence-redacted-beforeevent with no actor and no quest. To delete sooner, or per quest, keep usingattempts.redactEvidenceBefore. -
59cf5db:
management.identityIntegrations.createIntoAppSecret(input, name)andaddSecretIntoAppSecret(id, name)create an app login key, or add a secret to one, and store the new secret as the hosted app secretnamein the key's environment instead of returning it. The response hasappSecretand notoken. They need publish permission, and like every app login key change they work only from an organization administrator's Console session. When the app secret is refused, no key or secret is created. The CLI skill reference and capabilities describe Console's matching "Also save it as an app secret" option. -
0b1f8de: Operators can ban a member, and optionally take back what they earned. While banned, a member's attempts, completions and invitation acceptances are refused with 403
member-banned, their invitation code answersinvitation-not-found, attempts waiting for a decision are rejected, tracker records that would count for them are skipped withmember-banned, leaderboards and settlements leave them out, and referral payouts to and from them stop.relay.me.get()now answerseligible, false while the member is banned (true from older Relay versions).management.members.ban(member, { reason, rollback })bans, whererollbackcan revoke all their completions, reverse the referral payouts they received, and reverse manual awards, imported opening balances and engagement payouts, a step at a time in the background.management.members.previewBan(member, { rollback })previews it per point ledger, andmanagement.members.unban(member, { reason, restoreCompletions })lifts it. Banning and unbanning needreviewpermission; reversing referral payouts or point awards also needspublish.members.getanswers the member's latest ban asban, and member lists answerbannedAt. The CLI addsdomino members ban(with--rollbackand--dry-run) anddomino members unban. Member bans are experimental. Member imports refuse rows naming a banned member withmember-banned.
Patch Changes
- 166254b: App login now works on a private staging address.
createParticipantBackendno longer sendsX-Relay-App-Originwith its proxied calls, and Relay selects a request that carries an app login key by the key even when it also names an app origin, so a Test key registered forhttps://<slug>--staging.domino.pagesigns members in there without a workaround. Apps on SDK 1.63.0 work without upgrading. Thestaging-access-requirederror reference says a request with an app login key needs no staging pass. - ff6c976: The editor help for
useSessionandcreateParticipantBackendnow says what an app with its own login does when it signs members in or out without a page load: calluseSession().refresh()after its own sign-in route succeeds and again after its logout route, and sign out through the app rather thanuseSignOut(). - 7bd9a79: A refused ID now says what it may hold: an
actionId, reference or other ID with a:,.or space is refused with "use 1–100 letters, digits, _ or - (no :, . or spaces)" instead of a regular expression. A management call refused before sending puts each failing field and its reason on one line, such asInvalid management request: actionId: use 1–100 letters, digits, _ or - (no :, . or spaces);kind,code(invalid-input) andcauseare unchanged. Relay's own refusal of an invalid request body names the failing fields too, such asInvalid request: actionId, with the same status andcode.
1.63.0
Minor Changes
-
1fa78d2:
limitsreads Relay's platform limits asnumber, so a 1.x release can raise one or add one (released SDKs keep checking the values they shipped with, so Relay answers them within those):limits.quests,limits.questTypes,limits.collections,limits.sdkRuntime,limits.memberCompletions,limits.dashboardPanels,limits.dashboards,limits.defer,limits.retryCooldown,limits.analyticsQueryRows,limits.apps(secrets, KV and SQL stores, migrations, storage browsing and schedules),limits.stagedDeployment,limits.capabilityCalls,limits.trackerData,limits.trackerPreview,limits.walletsPerChain,limits.walletEventLogsandlimits.walletTransactions. It is exported from@domino-sdk/relay/schema,/authoring,/serverand/testing, with itsLimitstype.Deprecated, with unchanged values and types: the literal-typed limit constants
limitsreplaces, which can never change in 1.x. They arequestScaleLimits,sdkRuntimeLimits,maxMemberCompletions,dashboardLimits,deferPolicy,retryCooldownPolicy,analyticsQueryLimit,APP_SECRET_LIMIT,APP_KV_LIMIT,APP_SQL_LIMIT,APP_MIGRATION_LIMIT,APP_MIGRATION_FILE_BYTES,APP_MIGRATION_TOTAL_BYTES,APP_STORAGE_ROW_LIMIT,APP_STORAGE_PREVIEW_CHARACTERS,APP_STORAGE_CELL_CHARACTERS,stagedDeploymentLimits,APP_SCHEDULE_LIMIT,APP_SCHEDULE_MIN_INTERVAL,APP_SCHEDULE_TIMEOUT,APP_SCHEDULE_RESULT_BYTES,maxCapabilityCalls,trackerDataMaxBranches,trackerDataMaxEntries,trackerPreviewLimits,WALLETS_PER_CHAIN,maxWalletEventLogsandmaxWalletTransactions.Deprecated and removed in 2.0.0: Relay's own implementation helpers, which apps and quests have no use for. From
@domino-sdk/relay/server:participantRoutes,participantRoutePattern,sequenceCursorSchema,sourceDataSchema,trackerDataIssue,trackerDataIssues,whereSchemaIssue,validateQuestShape,containsReferralIntegrationReward,REFERRAL_INTEGRATION_REWARD_ERRORandnextScheduleRun. From@domino-sdk/relay/schema:dashboardIssue,normalizeDashboard,compatibleSettingKinds,isBoundSetting,whereSettingsandsourceContent. They keep working until then.zfrom@domino-sdk/relay/authoringstays zod 4 for all of 1.x. -
052744c:
inspectIntegrationKey({ baseUrl, integrationKey })from@domino-sdk/relay/portal-proxyanswers which app login key adi_key is: itsid,name,organization,project,environment,origin,identityKind,vouchesAccounts,secretExpiresAtandappOrigins, the addresses Domino serves the project's app from in that environment. It calls the newGET /v1/auth/integration, never returns a secret, and does not count as a use of the key. An unknown, expired or revoked key throwsParticipantExchangeErrorwith 401invalid-credential.identityIntegrationKeySchemaand theIdentityIntegrationKeytype are exported from@domino-sdk/relay/server.Behavior change:
domino secrets setnow checks a value that starts withdi_before storing it. It refuses, storing nothing and exiting 2 withINVALID_ARGUMENT, a key that is unknown, expired or revoked, or that belongs to another project or environment, such as a Live key set as Test's secret. It stores a key whose origin is not the app's own address for that environment with a warning, and a key it could not check (network failure, or a Relay without the route) with a warning. A checked key is named in the result's newidentityIntegrationfield. Other values are stored as before. -
e71245d:
createParticipantBackendacceptsonExchangeError(error, request), called with each refused sign-in exchange as Domino answered it (aParticipantExchangeErrorwithstatus,code, Domino'smessageandretryAfter) before the browser gets its answer, so the app's server can log why sign-in failed. The browser's answer is unchanged, also when the callback throws.Behavior change: creating an app login key for one of Domino's own app addresses in the environment that address does not serve is now refused with 400
invalid-request, and the message names the right environment. A<project-slug>--staging.domino.pageor preview address belongs in Test, a live<project-slug>.domino.pageaddress in Live. Other origins, custom domains included, can still use either environment, and existing keys keep working. An origin that is a URL but not an exact HTTPS origin is refused with a message that names the origin it accepts ("Use the exact origin https://app.example.com, without a path, query or trailing slash.") or "Use an HTTPS origin." instead of "Use an exact HTTPS application origin"; over HTTP the management API now answers that message instead of "Invalid management request". The app login guide and the Domino skill references gain a troubleshooting section with the exchange refusal codes. -
eaaae2a: Test members that
resetrefuses because they redeemed an offer, hold a reservation or a started reward handover, or took part in a referral can now be purged:management.testMembers.purge({ actionId, members, reason })anddomino test-members purge MEMBER... --reason TEXTdelete up to 500 test members in one transaction, in the test environment only. The stock unit each redemption, reservation and pickup handover took goes back to its stock item, so the offer's stock and redeemed count stay consistent, and the members' referrals and referral accounting go with them. List both sides of every referral: one with a member outside the list refuses the call withtest-member-reset-blocked(referrals affect another member, naming the unlisted member inoutside). Every other reason reset refuses still applies, and a refused purge changes nothing. The audit records one event with the operator, the members, the reason and the stock returned.resetis unchanged. See Purge members reset refuses. -
69e017d: Erase a member on the person's request:
management.members.erase(member, { reason, actionId? })anddomino members erase MEMBER --reason TEXT [--yes](POST /management/v1/members/:member/erase,publishpermission, live and test). It can't be undone. The member's profile and uploaded avatar, sign-in identities, linked accounts and their tokens, app identities, wallets, sessions, consent, leaderboard aliases, referral code, the evidence of every attempt and the stored content of the tracker records their accounts authored are deleted; reasons people typed on the rows that stay read[erased]. The member ID keeps its points, completions, rewards and referrals. The result is{ member, erasedAt, deleted }; while an attempt waits for a decision or a reward handover is in progress it is refused with 409member-erase-blocked. See Erase a member on request.Member reads (
members.get,members.list) carry a newerasedAton an erased member, andmembers.getanswersprofile: nullfor it. After erasure, writes naming the member (points, badges, groups, campaigns, consent, profile changes) are refused with the new 409 codemember-erased, records naming its ID attribute to no one, and the same person signing in again becomes a new member. A member import's revert passes over an erased member's row and reports it inkeptLinkswith reasonmember-erased. The CLI command asks for confirmation in a terminal and needs--yesotherwise. -
bbc3623: Klaviyo is the first destination plugin: connected through Klaviyo's OAuth, it keeps opted-in members' Klaviyo profiles current with
domino_properties (points, ranks, quests completed, unredeemed rewards, campaigns), subscribes and unsubscribes them as their email consent changes (a backfill or resync never resubscribes someone Klaviyo has unsubscribed), and records Domino's events asDomino …metrics. It never calls Klaviyo's send, campaign or flow APIs. See the new guide, "Send reminders from Klaviyo".management.connectionsgains, experimental like the rest of it:connect(plugin, { config, dryRun: true })connects a destination that supports a dry run, in the test environment, without secrets or the vendor's account. It records what it would have sent instead of sending it.Connection.dryRunandPluginInfo.dryRunsay which. Refusals: 409dry-run-not-supported,dry-run-test-only.activity(plugin)reads a destination's delivery record: last sync, failing state, members synced and skipped per reason, the latest backfill or resync with its progress, and the latest 20 errors.resync(plugin)sends every member's profile and attributes again, never past events; one runs at a time.dryRunLog(plugin, { cursor, limit })pages through a dry run's requests, newest first.
These answer 409
not-a-destinationfor other plugins. The CLI addsdomino connections connect --dry-run,connections activity,connections resyncandconnections log. -
9cb68e2: A text setting can be secret:
settings.text({ label, secret: true }). Operators set its value in Console or withmanagement.quests.updateSettings, and the quest's code reads it as usual, but no management response returns it. Releases leave secret settings out ofvaluesandconfiguration, and list the ones that hold a value in the new optionalwithheldSettings. Sodomino deploy --json,--preview --json,quests list,releases get, deployments, attempts and member reads no longer print a shared code a guest must not see. Console shows a secret as Set or Not set, with a field to replace it and Clear. A secret setting takes no default: one with a non-emptydefault, or a value from a deploy entry'ssettings, is refused, and reusable quest types refuse secret settings. A secret's entry in a release'sfieldssayssecret: true. Settings withoutsecretbehave as before. Withholding follows each release's own fields, so releases and deployments from before a setting became secret still show its old value: set a new one after marking an existing setting secret. -
8ee88a9: Test members can redeem an offer:
management.testMembers.redeem(member, { offer }), with the offer's key or ID, anddomino test-members redeem MEMBER OFFER. The redemption runs as a signed-in member's would, with the same price, stock, per-member limit, requirements and refusals, writes the same redemption and entitlement, answers the redemption, and replays by action ID. It works in the test environment only, and the audit names the operator. A test member with a redemption can't be reset. -
6fe6f22: The testing kit lists and replays every capability reader, and a few small defaults change.
capabilityCalls.runQuest,createTestContextandreplayAttemptresults gaincapabilityCalls: every capability call as{ sequence, request, outcome }, with an openCapabilityCallRequestwhosekindnames the reader. It includes the callscallsleft out: Telegram, NFT, contract call, delegation, wallet event and transaction reads, and the member's state.callsis deprecated.- Replay divergences of any reader.
replayAttemptreports a call of any reader the capture cannot answer in the newcapabilityDivergence, and stops there unlessfactsanswers it. Before, such a call of a newer reader failed the replay withTestSetupError; now the replay resolves withdecision: null.divergenceis deprecated, and isnullwhen the first divergence is a newer reader's call. - Tolerant replay. A captured answer with a field or an enum value a later Relay added, such as a new field on a Discord message, now replays instead of failing. Answers in
factsare still checked strictly. - Reader fakes.
fakeWalletReader(facts)andfakeTelegramReader(facts)from@domino-sdk/relay/testingbuild readers with every method Relay supplies, for tests that assemble their own evaluation context. The readers added after 1.0 stay optional in their types; quest code calls them with a non-null assertion, as the snippets now do (ctx.wallet.nfts!(…),ctx.telegram!.connection!()), instead of guarding against a runtime that lacks them. - Behavior change: Telegram trackers fire once per record.
automatic()on atelegram.reactions()ortelegram.messages()tracker now defaults toon: "new-record"instead of"new-observation", so a reaction removed and added again no longer runs the quest again. Published quests keep the mode they were published with; republishing one with this SDK switches it unless it setson. domino checkwarnsTRIGGER_REFIRES_ON_REFRESHfor a non-content quest on a YouTube Shorts or TikTok tracker that runs on every observation: each refresh evaluates it again for every video. Usecontent({ tracker })withperformancefor a metric threshold.RelayProvidertakesfollowWallet.followWallet={false}keeps a wallet sign-in's session when the wallet switches account or locks. The default,true, signs out as before.- Behavior change:
unauthenticatedends the session. A participant backend's (createParticipantBackend) 401unauthenticatednow ends the member's session in the SDK's experience, progress and activity observers, asnot-signed-indoes, instead of reporting an error.RelayErrorCode.Unauthenticatednames it. enrollmentonnot-enrolledskips. Tracker trigger outcomes,relay.quests.outcomes()and automatic outcomes carry an optionalenrollmentonnot-enrolledskips:"quest"for the quest's own enrollment,"campaign"for its campaign's. A campaign skip on a tracker trigger now says "Member has not enrolled in the quest's campaign".- Naming. A leaderboard's
balanceis the ledger it ranks and its points pool'sledgerthe one the pool pays into; both are documented on the type. New fields that name a ledger are calledledger; existingbalancefields keep their name in 1.x.
-
561f4ab: Tiered rewards (
defineTieredReward) can be listed, archived and restored.management.resources.list()anddomino resources listlist them in a newtieredRewardsarray (managedTieredRewardSchema), besideoffers, anddomino tiered-rewards listlists them alone. The experimentalmanagement.tieredRewards.archiveandunarchive(POST /management/v1/tiered-rewards/REWARD/archive|unarchive) anddomino tiered-rewards archive|unarchive KEY --reasonarchive and restore one in test, by key or ID, withpublishpermission; a promotion carries it to live, and Console Settings → Resources lists them with the same controls. Each returns{ resource, changed }, typed bytieredRewardLifecycleResultSchema. While one is archived, a deploy or promotion that would make a quest, offer or leaderboard prize newly grant it fails with 422tiered-reward-archived, naming it inreward. What already granted it keeps granting it, its entitlements and handover keep working, and its key stays taken. Console Rewards leaves archived tiered rewards out of its rewards. Promotions now list tiered rewards as definitions of kindtiered-reward, and a rollback archives one the earlier promotion did not carry. -
8d79fd6: Four operations that had two public paths with different results now agree, or say how they differ:
management.deployments.promote,submitandrejectare deprecated, removed in 2.0.0.promoteprepares a promotion of all of test and publishes nothing; its JSDoc now says so, and that the answeredidis the promotion's, read withpromotions.getand rejected bydeployments.getandpublish. Usemanagement.promotions.prepare()with a client for test, andpromotions.rejectto decline.submitandrejectact only on live deployments made before live refused them.domino deploy --promoteis deprecated in favor ofdomino promote --environment test, and prints a warning naming it.management.testMembers.acceptAnyway(member, { attempt, rules: "live", reason })applies liveattempts.acceptAnyway's rules in test:reasonis required, and an archived quest, a superseded attempt, a completion cooldown, an exhausted budget, an existing or revoked completion and a used key are refused with live's 409 codes, leaving the attempt and balances unchanged. The default (rules: "test") still accepts those, with no reason, so a tester can get a test member unstuck. Areasonis recorded with the decision either way.domino test-members accept-anyway MEMBER ATTEMPT --live-rules --reason TEXTsends it.GET /v1/reward-deliveriesandGET /management/v1/reward-deliveriesansweritemsbesidedeliveries, with the same rows, sopaginate((cursor) => relay.me.rewardDeliveries({ cursor }))andmanagement.rewardDeliveries({ cursor })read every page. Both clients fillitemsfromdeliverieswhen an older Relay sends onlydeliveries.useExperience().deliveriesreads the role deliveries again on eachdelivery.updatedactivity event, so a role an operator revoked shows without waiting for focus.defineLeaderboardprints a warning, anddomino checkreports aLEADERBOARD_TIES_DIFFERwarning, when a board's livetiesdiffers from itssettle.ties, including a board that sets onlysettle.ties: "earliest", whose standings share ranks while its prizes pay first-to-reach.leaderboardTiesWarning(board)from@domino-sdk/relay/authoringreturns that message, or null.domino check'sdiagnosticsnow include warnings from data definitions beside its errors; only errors fail it.
Patch Changes
- cc04dae: Browser apps bundle less of the SDK: an app's bundler now drops the schemas it never reads, such as tracker, wallet-read and management schemas, instead of keeping every schema in each module the client imports.
@domino-sdk/relay/reactadds about 41 KB less minified JavaScript (235 KB instead of 276 KB), and@domino-sdk/relay/browserabout 42 KB less. Exports and behavior are unchanged. - 4842e30: Wording only. The app identity kind example is now
app:game, including the validation message "Name an app identity kind like app:game: …", since a Telegram account is vouched for rather than modelled as an app identity. The JSDoc ofprogress(),quests.claim,me.claimand the quest interactions says which meaning of "progress" and "claim" each uses, anddomino test-members passhelp callsprogress(...)quests counter quests. Examples use the@XDevelopersX account. The changelogs mark the versions that were never published to npm. - 425e4df: Internal: the "HTTPS, or plain HTTP on a loopback host" check for API URLs, sign-in URLs and portal proxy origins now has one implementation, shared through
@domino-sdk/relay/internal. Published behavior is unchanged. - 8515f3f:
participantResponsesandcapabilityResponsesfrom@domino-sdk/relay/schemanow build each reader on first use, asmanagementResponsesand the field-tolerant tables already do. Each reader is built once and stays writable; types and parse results are unchanged. - 8278803: Relay now refuses a malformed personal API key ID with 400
invalid-request("Use the key's ID, such as ak_…") onDELETE /management/v1/tokens/:idandPUT /management/v1/tokens/:id/scope. A key ID isak_followed by letters, digits or_. IDs fromtokens.list()anddomino tokens listalready have that form, so no working call changes: an unknownak_…ID still revokes as a silent success, and a key that belongs to someone else is still refused with 403. - eb8572b: The testing kit's participant API answers an activity cursor it cannot read as Relay does: 400
invalid-cursor, "Invalid cursor. Start from the first page.", instead ofinvalid-request.
1.62.0
Minor Changes
-
a341b16: Analytics counts Relay's own
key-usedrejection, of an attempt whose completion key another member's completion holds, as its own rejection source.rejectionsBySourcegainskeyUsed, andanalytics.query'ssourcebreakdown akeyUsedrow;evaluatorno longer includes them, while an evaluator's ownkey-usedstill counts there. EachtopReasonsitem carriescodeSource(relay,evaluator, or null without a code), and Relay'skey-usedand an evaluator's are separate rows, intopReasonsand in thereasonbreakdown. Both fields are optional in the schema, since older servers don't send them.domino analytics getmarks the code of a top reason Relay wrote, such as[relay key-used]or[relay budget-exhausted]. -
bd622ae: An app login's server can vouch for its members' X, Telegram and Discord accounts and wallets when it signs them in.
createParticipantBackend'sauthenticatemay returnaccountsnext tosubject, andonAccountsreceives each account's outcome (attached,already-linked, orrefusedwith a member import refusal code). The newexchangeParticipantfrom@domino-sdk/relay/portal-proxyruns the same exchange from a server without a browser and throwsParticipantExchangeError, aRequestErrorwith the refusal'sstatus,codeandretryAfterin milliseconds (isParticipantExchangeErrorrecognizes it). The key needs Vouches accounts, whichmanagement.identityIntegrations.setAccountVouching(id, { enabled, reason })turns on from a Console session with publish permission; identity integrations report it asvouchesAccounts. Vouched accounts and wallets carryvouchedBy: { integration, integrationName }next toimported: true. -
76be180: An attempt carries
codeSource:relaywhen Relay wrote itscode,evaluatorwhen the evaluator'sdecision.reject({ reason, code })did, and null without a code (AttemptCodeSource, open).reservedAttemptCodesno longer grows, so a quest built with any 1.x SDK keeps working when Relay adds a code:key-usedstays usable as an evaluator's reject code, andcodeSourcetells it apart from Relay's own shared-key rejection. Members still read a rejected attempt's code only when Relay rejected it. -
5233be5: The experimental billing results read the plan as
BillingPlan(KnownBillingPlan | (string & {})):subscription.planandscheduledChange.planinmanagement.billing.read(),cancel(),resume()andorganization(),billingSubscriptionSchema,projectBillingSchemaandorganizationBillingSchema, and the--jsonresults ofdomino billing show,billing organization,billing cancelandbilling resume, so a plan Relay adds no longer fails to parse. Handle a plan name this SDK does not know. The stablemanagement.usage.plan()anddomino usage planare unchanged: thesubscriptionthey carry keeps the plans this SDK knows (launch,growth,scale). -
3665a4e: Billing is experimental while it is open to Domino staff only: the
management.billingmethods, the billing schemas and types in@domino-sdk/relay/server, and thedomino billingcommands may change in a minor release, plan names included.domino billingnow appears under Experimental commands.management.billing.checkout()andchangePlan()take any plan name (BillingPlan,KnownBillingPlan | (string & {})), anddomino billing checkoutandbilling changetake any--plan; Relay refuses a plan it does not offer with 400, as before.management.usage.plan()anddomino usage planstay stable.The CLI's JSON result schemas now leave objects open, as documented, so a JSON Schema validator such as Ajv accepts fields a later release adds; only the variants of a
oneOfstay closed.webhooks deliveries,storage rowsandstorage keysresults declare the optionalnextCursorthe API already sent.secrets,storageandschedulesresults includeenvironment, and these commands warn on stderr whenRELAY_ENVIRONMENTorrelay.jsonnames live while they use test.contract.jsonmarks deprecated options withdeprecated, as it does commands, andcapabilities.jsonno longer lists deprecated commands.management.referrals.controlandreferralControlSchemanow point topauseandresumeinstead of the deprecatedsuspend. -
361a5b1: A deployment keeps the active release of a quest it doesn't change, instead of publishing an equal one with a new ID. Deployment previews and results carry
questChanges: each quest by key,created,updated(with the release it replaces) orunchanged.domino deploylists the created and updated quests in its confirmation and in the--preview --jsonsummary, so a deploy that would replace a quest you didn't touch shows before it publishes. -
154af63: Discord message and reaction records name a thread's parent channel:
DiscordMessageDataandDiscordReactionDatagainparentChannelId, the channel a thread belongs to (a forum post's forum), ornulloutside a thread. It is absent when it is unknown, because Discord's channel read failed.channelIdstays the thread's own ID. A trigger'swhere: { path: "parentChannelId", equals }selects a channel's threads; to count a channel and its threads, compare both fields in the evaluator.management.testMembers.recordanddomino test-members record --datatakeparentChannelIdfor Discord records. -
79b7c68:
keyed(fn, { across: "members" })makes a quest's completion keys unique across members: each key completes the quest once in the project and environment, for whichever member's completion commits first, such as a single-use code or a post that pays once. The release's completion is{ kind: "keyed", across: "members" }, and quest progress and experience reads carry it. Another member's attempt with a used key is rejected withkey-usedandcodeSource: "relay", which members see;attempts.acceptAnywayrefuses it with 409key-usedwhile the key is held. Revoking the holder's completion withfreeOccurrence: truefrees the key. Member imports refuse a row withMemberImportRefusalCode.KeyUsedwhen another member or an earlier row already uses one of its keys. -
56a0601:
fakeEvaluationMember(methods)from@domino-sdk/relay/testingbuilds anEvaluationMemberfor a test that assembles its own evaluation context. Pass the methods your code reads; the rest answer for a member with no linked accounts, completions, groups or enrollments, andbalanceandreferralsfail with aCapabilityErrorof codeauthor-error. A member written by hand stops compiling when a minor release adds a method; one fromfakeEvaluationMemberdoes not.The CLI's automation contract (
cli/contract/contract.json) lists the JSON Schemas ofrelay.jsonand the checkout'sdomino.jsonunderfiles, so a later release is checked to accept every file an older CLI wrote. -
5cc0d4d: Revoking a completion with
freeOccurrence: truenow returns the budget it used. Each quest budget the completion was charged gets that charge back, in the same transaction and in the budget period it was charged to, so the freed occurrence can be completed again within the cap. A point-priced budget gets back its charge less the points the revoke couldn't reverse.CompletionRevocationreports this in a new optionalbudgets(budget,released). Revoking without freeing the occurrence leaves budgets spent, as before. Completions recorded before this release give no budget back and reportbudgets: [].Restoring such a revocation charges that budget again and reports it in a new optional
CompletionRestoration.budgets(budget,charged). The charge is checked against the budget's current limit, or the limit it was first charged under if the quest removed the budget or changed its period or cost. Restore now also refuses with 409budget-exhausted, naming thebudget, when a budget has no room left for the charge, and then changes nothing. This is a behaviour change: until now, budgets stayed spent after any revoke. -
f9c9f34:
defineOffer,defineLeaderboard(prizes) anddefineReferral(inviter, invitee and milestone rewards) now throw when given abadge()orgroupMembership()reward, sodomino checkreports it before a deploy. These definitions type-checked but a deployment always refused them; now they fail when defined, withBadge rewards can only be granted by quests or manually, not by offers(orleaderboard prizes,referral programs), orGroup membership rewards can only be granted by quests, not by …. A deployment refuses them with the same message and 422validation-failed, instead of claiming a tier reward was needed.badgeRewardandgroupRewardfrom@domino-sdk/relay/schemaare deprecated: usebadge()andgroupMembership()from@domino-sdk/relay/authoring.defineOfferfrom@domino-sdk/relay/schemais deprecated too and keeps its old behaviour; import it from@domino-sdk/relay/authoring, which does the check. -
dcc32da:
ctx.wallet.events()takesnewest, from 1 to 1,000: it returns at most that many of the newest matching events, reading back from the newest block, and stops at the 3,000,000-block or 1,000-log cap instead of throwingauthor-error.WalletEventsgains an optionalcomplete,falsewhen anewestread left older matching events out or stopped beforesince. A busy wallet, or a long window on a fast chain such as Arbitrum One (3,000,000 blocks is about 9 days there), no longer has to fail the attempt. A call withoutnewestbehaves as before: it returns every matching event, now withcomplete: true, or throwsauthor-error. A read whose start-block estimate overshot 3,000,000 blocks while the span itself fit is no longer refused. Theonchain-actionsnippet reads withnewest: maxWalletEventLogs. -
98ad384: A member import carries its real state in a new
statefield:open,checking,checked,applying,applied,stopped,reverting,revertedorcanceled, typed open (KnownMemberImportState | (string & {}), withMemberImportStatelisting the known values), so Relay may add states.statusis unchanged and still readsappliedfor a stopped, reverting or reverted import, so code filtering onstatus === "applied"counts those too; readstateinstead. Import rows gainreverted, true once the import's revert went through the row. Both fields are optional in the schema, since older servers don't send them.domino importanddomino importsresults includestate, and the CLI polls on it.MemberSummary(relay.me(),useMe()) now typesemail,emailsandclockas required, since Relay always sends them: code no longer needs to check them forundefined. The SDK reads a/v1/meresponse from a Relay older than these fields withemail: null,emails: []and aclockat real time (offset 0), the same as code treating them as absent did.memberTime()still accepts an object withoutclock.MemberSummary,LeaderboardPage,LeaderboardHistoryPage,Badge,MemberBadgeandMemberBadgesare marked as response types Relay may add properties to. -
4bf82e5: More management writes are safe to retry.
memberImports.check,apply,cancelandstop, andpromotions.approve, take an optional{ actionId }before the call options (check(id, { actionId });check(id, call)still works), and so doeswebhooks.replay(id, delivery, { actionId }).promotions.reject,members.unlinkCreatorAccountandidentityIntegrations.setAccountVouchingtakeactionIdin their input. With an action ID, a retry answers with the first result instead of a refusal for the state the first call left. These methods now generate an action ID when you pass none, as other writes do, so the client retries them after a lost answer.Inputs fenced by
revisionalso take it asexpectedRevision, the name newer methods use: quest and redemptionpauseandresume,offers.control,offers.cancelRedemption,handover.beginandcancel,entitlements.void,restoreandcorrectCollection, the pickup policy,members.unlinkCreatorAccountand the deprecated account unlinks. Pass either; a call that names two different revisions is refused.expectedRevision()and theExpectedRevisiontype are exported from@domino-sdk/relay/server.attempts.acceptAnyway,attempts.redactEvidence,completions.revokeandcompletions.restoretake their path ID first, as the route names it:acceptAnyway(attempt, { reason }). The object form,acceptAnyway({ attempt, reason }), keeps working. -
4bf82e5:
relay.badges.catalog()reads the project's active badge catalog as{ items }, likerelay.me.badges(), from the newGET /v1/badges/catalog.relay.badges.list()andGET /v1/badgeskeep answering the bare array and are deprecated.useBadgeCatalog()now reads the new route and still returns the array.createFakeRelaytakes abadgesseed and answers both routes from it.relay.auth.exchange(),ExchangeInputandexchangeSchemaare deprecated:POST /v1/auth/exchangerefuses the body they send with 400, because the route takes{ subject, accounts?, previousToken? }with an identity integration server key. Sign a user in from a server withexchangeParticipantfrom@domino-sdk/relay/portal-proxy.The activity feed's JSDoc, and
paginate()'s, now say thatme.activity.listnever answers a nullnextCursor, so a loop over it stops on its own condition, such as an empty page or a count. -
4bf82e5: ISO times in management responses carry their epoch milliseconds beside them, named
<field>Ms.analytics.readandanalytics.query(and so eachdashboards.readpanel) sendrange.fromMs,range.toMs, each bucket'sstartMsandendMs,previous.fromMsandprevious.toMs, andtestEnvironment.lastResetAtMs; custom domains (management.domains.list|get|add|recheck) sendcheckedAtMs,nextCheckAtMs,createdAtMsandcloudflare.certificateExpiresAtMs. The ISO fields are unchanged, and the new fields are optional in the schemas, since older servers don't send them.management.testMembers.advance(member, { actionId, to })takestoas epoch milliseconds as well as an ISO 8601 instant with an offset;testMemberClockAdvanceSchemaparses epoch milliseconds to that instant as an ISO string in UTC, so its parsedtostays a string. -
ea2c9be: A retention job can delete the evidence of a quest's attempts decided before a cutoff:
management.attempts.redactEvidenceBefore({ quest, decidedBefore, reason })(reviewpermission; in live it also requires an organization administrator's Console session or personal key, or a service key, and refuses any other caller with403 permission-required, because the deletion can't be undone) deletes up to 100 attempts' evidence per call, oldest decision first, exactly asattempts.redactEvidencedoes for one, and answersmore: truewhile others remain. Attempts still waiting for a decision are skipped, and adecidedBeforein the future is refused with400 invalid-request. Each call records oneevidence-redacted-beforeaudit event.domino attempts redact-evidence-before --quest QUEST --decided-before MS --reason TEXTrepeats the call until nothing is left and prints the total. -
2de5d71:
@domino-sdk/relay/testingaddscreateFakeRelay(options), an experimental in-memory participant API for UI tests of apps built on@domino-sdk/relay/react. Seed it with amember(ornullfor signed out),quests,balances,activity,leaderboardsand the rest; it answers the session, logout, experience, points, point history, point ledgers, activity, leaderboard standings and the server integration exchange, under/v1/...and/relay/v1/..., and returnshandle(request)for a Playwright or MSW route handler,fetchforcreateRelayClient, andrequests, every request it answered. Each answer is checked against the SDK's response schema, so a seed Relay could never send throws aTestSetupErrornaming the route. Another participant route answers501with codenot-fakeduntilroutesanswers it. The testing guide lists the requestsRelayProvidermakes on mount and while polling.createFakeRelayand itsFakeRelay,FakeRelayOptionsandFakeRelayRequesttypes are tagged@experimental, outside the 1.x compatibility promise: a minor release can change them. -
e58c2ae: Name a member's account the way a member import does, everywhere an operator command takes one:
{ kind: "x" | "discord" | "telegram", subject }with the numeric user ID,{ kind: "app:<name>", subject }, or{ kind: "wallet", chain, address }(identityReferenceSchema,IdentityReference).members.identityStates.unlinkandrelinktake it asidentity(memberIdentityStateChangeInputSchema,MemberIdentityStateChangeInput);setSignInalso takes a Cosmos wallet with itsnetwork. An X or Discord subject names the member's selected account with that subject, else the only one they hold. A subject the member holds through several issuers, none of them selected, fails with 409identity-ambiguous; name it byissuerthen.members.lookuptakes it, and sends it as theproviderandsubjectevery Relay version reads.testMembers.linkAccountandunlinkAccounttake{ actionId, kind, subject }for an X or Discord account.- The CLI takes
--kindand--subjectondomino members lookup(with--chainand--addressfor a wallet) and ondomino test-members link-accountandunlink-account.
Deprecated, removed in 2.0.0: the
{ kind: "oauth", provider, issuer, subject }and{ kind: "app", appKind, subject }input selectors,memberIdentityLinkChangeInputSchemaandMemberIdentityLinkChangeInput,members.lookup({ provider, subject }), the test-member{ provider, id }, and the CLI'smembers lookup --providerandtest-members link-account|unlink-account --provider|--id. They keep working through 1.x;contract.jsonmarks the deprecated options withdeprecated.Identity reads gain an optional
source:member,import,app-loginortest-link, an open string (IdentitySource,KnownIdentitySource), andimport, the ID of the member import that attached the identity when Relay recorded it. Identity states of every kind carry them, and so domembers.get'swalletsandidentitiesand the deprecatedoauthAccountsandtelegramAccount.importedis unchanged. -
3a3feef: A leaderboard can rank its live standings first-to-reach.
defineLeaderboard({ ..., ties: "earliest" })orders equal scores by each member's latest qualifying entry, earliest first, then by member ID, and gives every member who scored their own rank: the order a settlement withsettle.ties: "earliest"freezes. Standings pages,me,nearby, placement, and the management and CLI standings reads all follow it, so a tie-broken board is one paged read.tiesis optional. Omitted or"shared", equal scores share a rank as before, so published boards rank unchanged. It is independent ofsettle.ties; set both on a board that settles so members see the order prizes follow. Members listed throughincludeUnscoredstill share one rank after every member who scored. -
7a93f76:
createParticipantBackendfrom@domino-sdk/relay/portal-proxyaccepts a loopback HTTP app origin (http://localhost,http://127.0.0.1orhttp://[::1], any port), so a browser test can run the real adapter, identity exchange included, at a local address againstcreateFakeRelay'sfetch. On such an origin thedomino_app_sessioncookie omitsSecure. Any otherhttp:origin still throws,baseUrlmust still be HTTPS, and Relay still registers only HTTPS app origins. -
1a70098:
management.members.lookup({ provider: "app:<name>", subject })finds the member holding a linked app identity, so a server can map one of its user IDs to the member.members.people(),members.list(),members.lookup()andmembers.get()list each member's linkedappIdentities({ kind, subject }), andmembers.list({ q })also matches part of an app identity's subject. Identities an operator unlinked are neither found nor listed. -
dd7ced3: Experimental: a member import row can carry the member's marketing consent from the platform they come from. The optional
consentlist onmemberImportRowSchematakes one{ channel, state: "granted" | "withdrawn", at, wording? }per channel; applying the row records it with its original time and wording, markedsource: "imported", in the row's transaction, unless the member chose on that channel in Relay (their choice always stays) or an earlier import recorded consent as recent or more recent. A check refuses the row withfuture-actionfor a time in the future,unsupported-consent-channelfor a channel other thanemail,consent-wording-required,consent-wording-too-longorconsent-wording-invalidfor the wording, andduplicate-entryfor a channel named twice.KnownConsentSourcegainsimported. The CLI imports reference describes the field. -
5a34b36: Experimental: members record their marketing consent per channel.
relay.consent.get()lists the signed-in member's current consent, one entry per channel they chose on (member,channel,state,at,wording,source), andrelay.consent.set({ channel, granted, wording, actionId? })grants or withdraws one channel. TheuseConsent()React hook returns the query withgrant(channel, wording)andwithdraw(channel, wording?). Relay stamps the time and stores the wording verbatim; a repeat of the current state and wording records nothing. Onlyemailis accepted for now: other channels are refused withunsupported-consent-channel, a grant without wording withconsent-wording-required, wording over 2,000 characters withconsent-wording-too-long, and wording with a NUL or a lone surrogate withconsent-wording-invalid. Channel, state and source are open strings in results. The consent API is@experimentaland can change in a minor release. The participant API reference andcapabilities.jsondescribe it. -
4b7c062: Integration modules, reward kinds and webhook event types now have open types beside SDK 1.x's closed ones, which keep their exact shapes.
defineQuestaccepts an integration module whose provider the SDK does not know, such as{ provider: "future-chat" }.IntegrationModule.providerisIntegrationModuleProvider,KnownIntegrationModuleProvider | (string & {}). The quest builds; its description keepsintegrationsto the modules the SDK knows and addsintegrationModules, every declared module. Publishing a quest that declares a module Relay does not have is refused with 422unknown-integration-module. Discord and X quests behave as before. Evaluator contexts type readers only for the modules the SDK knows, now namedKnownIntegrationModule:EvaluationContext's module parameter takesreadonly KnownIntegrationModule[], so a helper that passedreadonly IntegrationModule[]to it explicitly passesreadonly KnownIntegrationModule[]instead.- Release reads (
management.releases.get,releases.list, publish results and every otherPublishedRelease) addintegrationModulesandrewardEntries.rewardEntrieslists every reward as aRewardEntry,{ type, data }, with an openRewardType; a reward kind the SDK does not know, which a later Relay adds, will appear only there and never inrewards.management.quests.previewAutomaticCatchUp()adds the samerewardEntriesbesiderewards. Older Relay versions omit these fields. ActivityType(KnownActivityType | (string & {})) from@domino-sdk/relay/servernames the webhook and activity event types for your own handlers;WebhookEvent.typestays astring. Ignore a type you don't handle: Relay adds event types.
-
de99154: Every sign-in provider Relay has, Telegram included, now has open surfaces beside the closed
AuthProviderlists, which keep their exact shapes.SignInProviderisKnownSignInProvider | (string & {}), so handle a provider you do not know.relay.auth.providers()addssignInProviders,[{ provider, configured }]for every sign-in provider.management.participantSignIn()adds the samesignInProviders. Older Relay versions omit both.relay.auth.signInAccounts()(GET /v1/auth/sign-in-accounts) returns the member's account on every sign-in provider asSignInAccounts, shaped likeAccountConnectionwith an openproviderandstatus.relay.auth.start({ provider, mode })andrelay.auth.disconnect(provider)take anySignInProvider, such as"telegram".useAccount(provider)from@domino-sdk/relay/reactreads one provider's account, withconnect(),disconnect()andisConnected.management.members.accounts(member)(GET /members/:member/accounts) returns{ accounts }, the member's X, Discord and Telegram selections asManagedAccounts with an openprovider.management.members.unlinkAccount(member, { provider, subject, expectedRevision, reason, actionId? })(POST /members/:member/accounts/unlink,reviewpermission) unlinks one and returns that provider's selection; a retry with the sameactionIdreturns the first result.
-
80e09c2: Experimental: operators read a member's marketing consent.
management.members.consent(member)lists the member's current consent, one entry per channel they chose on, andmanagement.members.consentHistory(member, { cursor, limit })pages through every change on every channel, newest first, as{ items, nextCursor }; each entry is{ member, channel, state, at, wording, source }, andmemberConsentHistorySchemaandmemberConsentHistoryQuerySchemaare exported from@domino-sdk/relay/schema. Both needreadpermission (GET /management/v1/members/:member/consentand/consent/history); a member who never chose reads as empty, and an unknown member fails with 404not-found.domino members consent MEMBERprints both, with--limitand--cursorfor the history. The Console member page shows the same, read-only. There is no management write: only the member grants or withdraws consent. The consent API is@experimentaland can change in a minor release. -
423b6fc: Plugin connections, experimental.
management.connections(list,get,connect,update,disconnect) connects a project to the plugins Domino writes, separately in test and live; a promotion never copies a connection. A connection's config is checked against the plugin's JSON Schema (422invalid-confignames the failingpath), its secrets are write-only and sealed at rest, every change takes anactionId, andupdateanddisconnecttake anexpectedRevision(409connection-changed). The new exports (connectionSchema,connectionPageSchema,Connection,ConnectionStatusand the connect, update and disconnect inputs) are experimental and may change in a minor release. The CLI adds the experimentaldomino connections list|get|connect|update|disconnect, test unless--environment live, reading secrets from stdin as JSON or at hidden prompts, never from arguments. -
01b789e: Experimental
management.plugins.list()answers the plugins a deployment runs, each with the public part of its manifest:name,slots, the config's JSON Schema (configSchema), its secret fields and the Domino event types a destination receives. The new exports (pluginSchema,pluginPageSchema,PluginInfo,PluginPage,PluginSlot,KnownPluginSlotandknownPluginSlots) are experimental and may change in a minor release. Console builds each plugin's connection form on Integrations from it. -
2a83792: Plugin OAuth connections, experimental. A plugin whose
plugins.list()entry hasoauthconnects through its vendor's OAuth instead of entered secrets:management.connections.authorize(plugin, { config, returnTo?, actionId? })answers{ authorizationUrl, expiresAt }, a person opens the URL and grants access, and the vendor's callback makes the connectionready. The URL works once, for ten minutes. Domino refreshes the tokens before they expire and never answers them; when the vendor refuses a refresh, the connection isattentionwith the newreconnectReason(revokedorexpired), and authorizing again reconnects it. Disconnecting deletes the tokens, then revokes them at the vendor; a failed revocation is logged.connectrefuses an OAuth plugin with 409oauth-required,authorizerefuses one connected with secrets with 409oauth-not-supported, and a deployment without the vendor's OAuth app with 409plugin-not-configured(oauth.configuredis false). New experimental exports:connectionAuthorizeInputSchema,connectionAuthorizationSchema,ConnectionAuthorizeInput,ConnectionAuthorization,ConnectionReconnectReason,KnownConnectionReconnectReasonandknownConnectionReconnectReasons. The CLI adds the experimentaldomino connections authorize <plugin> --config-json …, which prints the authorization URL. -
172c3b5: Experimental: a
viewerproject role and grant end dates, to share a project's dashboards with a sponsor or client. A viewer holds one permission,dashboards, which readsdashboards.listanddashboards.readand nothing else: no members, evidence, quests or settings. Every other role holds it too, andreadkeeps reading dashboards, so no existing person or key loses access.management.grants.settakesprojectGrantSchema:rolesmay nameviewer, and an optionalexpiresAt(epoch ms, or null for no end) ends the grant's access without anyone removing it. An ended grant stays ingrants.list(), and the access history records each end date. Released SDKs keep reading grants: a grant'sroleskeeps the roles 1.x names, so a viewer's is empty, and the newprojectRoleslists every role. Likewise a project inmanagement.access()keepsdashboardsout ofpermissionsand lists it in the newprojectPermissions.projectRoleSchema,projectPermissionSchema,projectRolePermissions,projectGrantSchemaand these fields are experimental.Console grants the viewer role and an end date under Settings → Access → Team, and shows an ended grant as Expired. A viewer's Console shows only Analytics, opened on the project's first dashboard. The CLI's agent references describe the role.
-
b2b8127: A full deployment to test warns
quests-not-in-bundlewith the keys of quests published from code that it leaves out, such as a starter's sample quests whose files were deleted: they stay as published until archived withdomino quests archive <key> --reason "…". Archived quests and quests created in Console are not listed. A deployment input withpartial: truedoesn't warn, anddomino deploy --onlysends it.domino stagenow prints the warnings of the deployment its build published, and its--jsonresult carries them inwarnings; a build's status (GET /builds/{id}) names that deployment indeployment. -
a381dde: Staff can delete the evidence a member submitted for a decided attempt:
management.attempts.redactEvidence({ attempt, reason })anddomino attempts redact-evidence ATTEMPT --reason TEXT(reviewpermission, test and live). The photo, text, URL or quiz answers, and what the quest's capabilities read from them, are deleted; the attempt keeps its outcome, completion and rewards. Attempts that submitted the same photo are redacted together.management.attempts.getanswers the newredaction({ at, actor, reason }). Undecided attempts are refused withattempt-not-decided, andattempts.retryrefuses a redacted attempt withevidence-redacted. -
4443124: Quest code can read whether the member linked Telegram:
ctx.telegram.connection()resolves to{ userId }, the numeric Telegram user ID, ornull. It needs no connected chat and doesn't ask Telegram; an account an app login vouched for or a member import brought in counts. It reads the account the attempt pinned, likemember(), and is captured for replay. The testing kit answers it fromfacts.telegram.connection, andTelegramConnectionis exported from@domino-sdk/relay/authoring. -
b9270ff: Telegram Login, the member's Telegram account and its unlink now go through the generic sign-in surfaces, and Telegram's own members for them are deprecated. They keep working, with their routes, through SDK 1.x and are removed in 2.0.0:
relay.auth.telegram.start(mode)→relay.auth.start({ provider: "telegram", mode }).relay.auth.telegram.account()→ the"telegram"entry ofrelay.auth.signInAccounts().relay.auth.telegram.disconnect()→relay.auth.disconnect("telegram").useTelegramAccount()→useAccount("telegram"). Inside a Mini App, link withrelay.auth.telegram.miniApp.connect(initData), then callinvalidate([relayKeys.accounts, relayKeys.experience])fromuseInvalidate().useSignIn().withTelegram()→useSignIn().withProvider("telegram");withProvidernow takes anySignInProvider.SignInOptions.telegramanduseSignInOptions().telegram,TelegramSignInOptionandSignInActions.telegram→signInProviders, below.relay.auth.providers().telegram.loginandmanagement.participantSignIn().telegram.login→ the"telegram"entry ofsignInProviders.TelegramSignInMethodsand the newParticipantSignInTelegramare written-out types, so editors show these two deprecations.management.members.telegramAccount(member)andunlinkTelegramAccount(member, input)→members.accounts(member)andmembers.unlinkAccount(member, { provider: "telegram", subject, expectedRevision, reason }).
useSignInOptions()andsignInOptions(relay)addsignInProviders, the open sibling of theoauthentries ofoptions: every sign-in provider Domino configured, Telegram included, in Relay's order, each aProviderSignInOption({ kind: "provider", id, label, icon, start() }). A newer Relay may list a provider this SDK does not know; its label is its name. Against a Relay withoutsignInProviders, the list holds its providers and Telegram Login.SignInMethodsaddssignInProvidersandSignInActionsan optionalprovider(provider)action. The starters render their provider buttons fromsignInProviders.Mini App sign-in (
relay.auth.telegram.miniApp,useSignIn().withTelegramMiniApp,providers().telegram.miniApp,participantSignIn().telegram.miniAppBotId) is not deprecated. -
90a20c6: Telegram is now a plugin:
management.connections.list()anddomino connections get telegraminclude it,readywhile the environment has a connected chat,attentionwith the chat's issue while Domino's bot can't read one, anddisconnectedwithout chats. Chats still connect only through the bot (management.telegram.connect(), unchanged), soconnections.connect,updateanddisconnectanswer 409plugin-managedfor Telegram and for any plugin that connects through its own flow.knownPluginSlotsaddsannouncement, the slot Telegram posts quest announcements through.management.plugins.list()items gainconsole:{ component }when Console shows the plugin's own component instead of the generated card, otherwise null; the schema defaults it to null, since older servers don't send it. -
8e8e24a: Telegram Mini App sign-in has its own entrypoint,
@domino-sdk/relay/telegram(experimental):withTelegram(relay)addsrelay.telegramto a client and answers the same client,relay.telegram.miniApp.signIn(initData)and.connect(initData)do whatrelay.auth.telegram.miniAppdid, andtelegramMiniAppInitData()reads the launch data. Apps that don't import it don't carry it.relay.auth.telegram.miniAppand thetelegramMiniAppInitDataexports of@domino-sdk/relay/browserand@domino-sdk/relay/reactare deprecated, keep working through 1.x and are removed in 2.0.0.Telegram Login and Mini App sign-in now run in the Telegram plugin, with no change to what they accept or answer: members linked before keep signing in as the same member, and
/v1/auth/telegram/mini-apptakes the same requests.management.plugins.list()anddomino connections get telegramlist one more slot for Telegram, whichknownPluginSlotsadds:identity(members sign in with it). -
52ed037: Telegram's trackers and
ctx.telegramnow come from the Telegram plugin, with no change to what they record or read:telegram.messages()andtelegram.reactions()trackers, the bot's webhook at/v1/integrations/telegram/updates, andctx.telegram.connection()andmember()behave as before, and published quests keep working.management.plugins.list()anddomino connections get telegramlist two more slots for Telegram, whichknownPluginSlotsadds:tracker-source(the plugin writes its trackers' records) andevaluator-capability(quest code reads it asctx.<plugin>). A quest may now declare thetelegramintegration module,{ provider: "telegram" }, and publishes; before, publishing refused it withunknown-integration-module. Declaring it is optional, since every quest getsctx.telegram. -
2fe2d72:
management.testMembers.history()anddomino test-members historyname each attempt's and skipped tracker outcome's quest by its authoring key asquestKey, next to the quest ID inquest. It is absent for a quest created without a key. A script can filter a test member's history by quest key without callingquests getfirst. -
7fa2a06: Test members can submit photo quests.
management.testMembers.uploadEvidence(member, file, { actionId })uploads a JPEG or PNG of at most 5 MB as the test member's photo, andactsubmits itsidasevidence.domino test-members act MEMBER QUEST --photo FILEdoes both, so the quest's real image check and the review queue can be proven from the CLI. The campaign-testing reference now points photo evaluator tests atfacts.images.review. -
06046f1: X trackers can pay the member whose post a collected post quotes or reposts.
automatic({ tracker, beneficiary: "quoted" })pays the quoted post's author andbeneficiary: "reposted"the reposted post's author, so a "get noticed" quest can reward members an account picks up:x.search({ query: "from:examplelabs filter:nativeretweets" })withbeneficiary: "reposted".xAccountRolesgains both. A post that is no quote or repost is skipped withno-account("Post is not a quote" / "Post is not a repost"), and a quote or repost of the author's own post withbeneficiary-is-author. LikeinReplyTo, these quests don't reward posting and don't raisex-post-rewards-ungated.xAccountRolesandSourceRoleslist the roles this SDK knows, and a later release may add roles to them, so code that switches over them needs a default branch.
Patch Changes
-
ee3f488: Bulk management writes (
catalog.deploy, catalog previews and drafts,deployments.preview,releases.publishBatch, tracker record ingest andmemberImports.addRows) refuse a body with more than 2,000,000 JSON values with 413payload-too-largebefore parsing it, whatever its byte size; other management writes refuse one past 500,000. A server handles one body over 4 MiB or 500,000 values at a time and answers another 503 withRetry-Afterwhen it has waited 10 seconds.memberImports.addRowsnow takes at most 64,000,000 bytes, like the other bulk writes, so a single batch above 64 MB from an older CLI gets 413.domino importnow splits rows into batches of at most 500 rows, 16 MB and 1,000,000 JSON values, sends a row larger than that in a batch of its own, and refuses a row too large for one call (63 MB or 1,990,000 values) before uploading anything, naming its line.The management client, the CLI and the participant client now send any call again after a 503 with
Retry-After, within its deadline and attempts, including writes without an action ID: Relay answers such a 503 only to a request it refused before doing anything. Before, those writes were sent once. -
f13cfb6: A
domino loginCLI session creates and scopes personal API keys in test only.management.tokens.createwithenvironment: "live", andmanagement.tokens.scopeon a live key, now answer 403console-approval-requiredfrom a CLI session, whateverX-Relay-Environmentselects: a live key would carry the live operations (points, attempt confirmations, reviews, pauses) that the CLI session itself is refused, and would outlivedomino logout. Create and change live API keys in Console (Settings → API keys). A CLI session still lists its keys and revokes any of them, live included, and Console sessions are unchanged.This changes a 1.x behaviour, an exception Peter Szoke granted on 2026-10-15: no request or response shape changes, and the refusal is a typed 403 that says where to go.
domino tokens create --environment liveprints that refusal, anddomino tokens --helpsays live keys come from Console. -
eed1808: Participant cookies on HTTPS now use
__Host-names (__Host-relay_sessionand the sign-in binding cookies), so one app ondomino.pagecan no longer set them for another, and a cookie name the browser sends twice reads as absent.createParticipantBackendforwards the OAuth return binding as__Host-relay_auth_returnwhen the browser has it, otherwiserelay_auth_return, and forwards neither when a name appears twice.Members signed in when this deploys are signed out once and sign in again, and a sign-in started just before the deploy fails once and starts over. Older SDK versions keep working: through an identity integration, Relay still sets and reads
relay_auth_return. -
94ca2b5: Unlinking an imported or vouched account now releases it.
management.members.identityStates.unlink(anddomino members unlink-identityand the deprecated per-kind unlink methods) on an X, Discord or Telegram account or wallet that a member import or an app login vouched for, and that the member never signed in with or proved, removes it from the member instead of keeping it bound: its owner can then link it and sign in with it. The answer shows it unlinked withcanRelink: false, and later imports and app-login vouching refuse it for that member withidentity-unlinked. Accounts the member signed in with or proved keep the previous behaviour: the link is cleared, ownership stays, and it can be relinked. -
d4fcade: Role delivery helpers ignore announcement rows.
useExperience().deliveries,roleDeliveryStatusand the watch behind them count onlygrant-roledeliveries, so a quest that only posts an announcement in a Telegram chat or Discord channel no longer shows "Role delivery pending" or "Role granted", and its pending post is no longer polled every 5 seconds. A quest with a Discord role reward reports its role as before, also when it announces.roleDeliveryStatusapplies the same rule to rawme.rewardDeliveries()rows, andRoleDeliverygains an optionalaction.me.rewardDeliveries()itself still returns every delivery. -
8064983: Deployment previews, records and history pages (
deploymentPreviewSchema,deploymentRecordSchema,deploymentHistoryPageSchema, and themanagement.deploymentsmethods that return them),collectionSlotSchemaandquestCollectionSchemaread collections without rejecting unknown fields, so a field a newer Relay adds to a deployment collection no longer fails the response. Collection inputs (collectionSlotDeclarationSchema,catalogDeploymentSchemaand saved draft changes) still reject unknown keys. -
980e6ca: Sign-in rate limits count an IPv6 client by its /64, so every address in one /64 shares the per-IP limits on email, wallet, OAuth and Telegram sign-in starts,
domino logindevice starts and staging password attempts. An IPv4-mapped IPv6 address counts as its IPv4 address. Per-address limits now count per client: three sign-in codes per email address and ten wallet challenges per wallet address from one IP address every ten minutes, so another client hitting them no longer refuses that address's own sign-in, on this project or another. Each project and environment also caps an address at ten sign-in codes and thirty wallet challenges every ten minutes from all clients together. -
d683d00: Sign-in rate limits per IP address count each member of a Domino-hosted app on their own. Hosted apps reach the API from Cloudflare's shared Worker address, so until now every member of every hosted app shared one set of per-IP limits. Domino now attests the visitor's IP on every request a hosted app's server sends to the API, with no app change or rebuild. A request from Cloudflare's shared Worker address without Domino's attestation, such as from an app you run on your own Cloudflare Workers, is counted per project and environment instead of with every other Worker.
-
081806c: Personal keys (
ak_…) and MCP OAuth tokens that Clerk does not recognize now count against the client that presented them, on the management API,gitand the MCP server: after 30 in 10 minutes from one IPv4 address or IPv6 /64, counted by each API server on its own, everyak_and MCP token from it answers 429rate-limitedwithRetry-Afteruntil the window ends. Expired, revoked and other refused credentials Clerk knows are not counted, and neither is Clerk refusing Domino's own secret key. A refused value presented again within a minute can be refused without asking Clerk, and a value no Clerk credential can be (under 16 characters after its prefix, over 4096, or with a character a bearer token cannot hold) is refused with 401invalid-credentialwithout asking Clerk. When Domino is checking many new credentials at once, a key or token it has not accepted recently can answer 503 withRetry-After: 1. Clerk's own rate limit now answers 503 with Clerk'sRetry-After. -
114e0c7: Telegram and Discord install links now tell whoever opens them which Domino project they connect to. The Telegram bot's reply to a link, before the chat picker, names the organization, the project's name and slug, and the environment, and so does its
Connected …reply. A Discord install link sent to a server's administrator (Send to your server admin) first opens a page naming the same, with a Continue to Discord link; the page changes nothing, so a link preview that fetches it stays harmless, and the page shown after connecting names the project too. Creating either link (management.telegram.connect(),POST /management/v1/integrations/discord/links) now reads the organization's name from Clerk, and answers 503 rather than issue a link that names nobody when Clerk can't answer. Links made before this change still work and name no project. Console's own Discord installation is unchanged. -
92ee725: Connecting a Discord server or Telegram chat where Domino's shared bot already holds rights now takes someone who holds those rights too. In Discord, installing the bot (Console's Connect Discord or a shared install link) refuses a user who is neither the server's owner nor an Administrator when the bot already has a permission there they lack, such as Manage Roles another organization gave it; the install page says "Domino's bot already has permissions in this server that you don't have. Ask a server administrator to connect it." and nothing is connected. A fresh install, where the bot holds nothing beyond
@everyone, still needs only Manage Server. In Telegram, an administrator who lacks an administrator right the bot holds in the chat, such as posting messages, is refused with a reply naming the missing rights; the chat's creator always connects it. Reusing a Discord server the organization already connected is unchanged, and existing connections are not revisited. -
10826dc: An organization administrator's CLI session lists the project's identity integrations (app login keys) and their claimed kinds:
domino identity-integrations listanddomino identity-integrations kinds, ormanagement.identityIntegrations.list()andkinds(). They return each key's origin, identity kind and secrets' expiry and last use, never a secret. Creating, rotating and revoking keys and their secrets stays in Console; non-admin CLI sessions and every key type are refused. -
c391c86: Documentation only: the JSDoc for
pointLedgers.create,pointLedgers.update,calendar.configure,profiles.configureand thecampaignsmethods now says what live refuses. In live, every definition write is refused for every credential, Console included, unless it applies an approved promotion;campaigns.enrollandcampaigns.withdraware operations that needenrolland work in live with a personal API key. -
1c61073:
domino tokens list|create|scope|revokemanage your personal API keys from adomino loginsession, asmanagement.tokensdoes.domino tokens create --name NAME --permission P [--ingest-tracker T] [--days N]issues a key for the selected project and environment and prints its one-timetokenand expiry;--jsonprints the SDK's answer, sodomino tokens create … --json | jq -r .token | domino secrets set NAMEstores it. A personal key expires afterdays, 1 to 90, default 30;--days 91is refused before anything is sent.domino tokens listshows each key's expiry. API keys still cannot manage keys (credential-not-allowed), and a credential that must not expire is a service key, which an organization admin creates in Console. -
aaf28f9: Quest submissions are limited to 120 an hour per member in each project environment, counted together across
POST /v1/me/attempts,POST /v1/attempts,POST /v1/me/submissions/:id/submitandPOST /v1/attempts/:id/retry. Past it they answer 429rate-limitedwithRetry-After, before the request is read. The quest controllers keep the saved draft, soresume()continues it. The participant reference in the Domino skill states the limit. -
9f39ea4: An operator's reversal of a referral payout stays reversed. Reversing all or part of a referral payout (Console point history Reverse, or
management.points.record({ kind: "reverse", entry })) writes those points off on the referral account, so no later reconcile pays them again; earlier, the inviter's next invitee activity or a new acceptance of their code paid the reversed amount back. Later bonus growth still pays, and restoring a revoked qualification does not repay the reversed points.earnedin the referral summary and the management referral accounts excludes the reversed points, so a reversed payout no longer reads as earned. -
b866593: Behavior change: completing a referral program's qualifying quest no longer closes the member's invitation. A friend who claimed an entry quest at sign-in, for example through
management.passes.verify, can still accept an invitation afterwards; accepting qualifies them in each enabled program whose quest they already completed, on their earliest unrevoked completion, and pays the inviter and the friend once. Completing any other quest still makesreferrals.acceptandtestMembers.acceptReferralrefuse withinvitation-closed, and a revoked qualifying completion neither closes the invitation nor qualifies. Points the friend earned before accepting don't earn the inviter a percentage bonus. -
1cdc64b: Staging a commit the project's hosted repository doesn't have (a mistyped SHA, or a commit not pushed yet) now answers a certain 422
commit-not-foundbefore any build is reserved, instead of an uncertain 502upstream-failed.domino stagereports it asREJECTEDwithapiCode: "commit-not-found", saves no operation to resume and prints no retry command: push the commit, then stage again. Other GitHub failures during the commit check still answer 502 and stay uncertain. -
3d2f654: Staging's participant API is now private, like staging itself. A participant request that names a staging address (
<slug>--staging.domino.page) inX-Relay-App-Originanswers only when it carries the staging pass, which the staging gateway gives browsers signed in to staging; anything else is refused with 403staging-access-required. Apps on staging keep working with no change: the gateway adds the pass to the requests it hands the app and to legacy/relay/*calls, andproxyParticipantforwards it with the browser's other cookies. Only callers that sent a staging origin straight to the API, outside the staging address, see the new refusal. The retiredstaging-<slug>.domino.runorigin now selects nothing (403 "Application origin is not registered or is offline"). Previews, live apps and identity integrations are unchanged.
1.61.0
Minor Changes
-
f0ce453:
RelayProviderfollows the EVM wallet a participant signed in with throughuseSignIn().withWallet()or a wallet option ofuseSignInOptions(), so the starters need no code for it. When the wallet switches to another account (accountsChanged, compared case-insensitively) or reports none because it was locked or disconnected, the provider signs out asuseSignOut()does and the app shows its sign-in again; it does not sign the new account in. The same account, including the event wallets send on first connect, changes nothing. After a reload the provider finds the wallet again by name and checkseth_accountswithout a prompt.WalletSignergains an optionalwatchAccounts(listener), whichevmSigner()implements, andEip1193Provideran optionalonandremoveListener. -
b369a75: A project connects a Discord server that another project of its organization already connects, ready, in the same environment, without installing Domino's bot again. Any credential with
publishpermission can: an automation key, a CLI session, a personal API key or Console (Integrations, Or reuse). Installing the bot into a new server stays with an organization administrator in Console.management.discord.servers()anddomino integrations discord serverslist the servers the project can connect.management.discord.connect(server)anddomino integrations discord connect --server <id>connect one, once the bot still sees it. They refuse with 409server-not-connected-in-organizationfor any other server, 409integration-connectedwhen the project already has a Discord connection, and 422server-not-reachablewhen the bot lost the server. Connecting the server the project already connects changes nothing.management.discord.connection()anddomino integrations discord statusread the project's Discord connection.
The project's audit records each connection as
discord-server-connected, with who connected it and the project whose connection it reused. An automation key and a CLI session connect only in test.
Patch Changes
- e71f881:
domino checkruns each badge, point ledger, offer, leaderboard, referral, dashboard and tracker entry in a separate Node process and parses its export with the schema Relay parses it with at deployment. A definition that fails, such as an offer withoutstock, reports aDEFINITION_INVALIDerror diagnostic with itsfile,export, fieldpathand message, and fails the check instead of failing later in staging.defineOffer,defineBadge,defineLedger,defineLeaderboard,defineReferral,defineDashboardanddefineTrackerstill throw aZodErrorfor invalid input, and its message now names the kind, key and fields, for exampleOffer "ladder" is invalid: stock: Invalid input: expected number, received undefined. Staging shows that message when its data setup fails. A deployment, preview or staging activation with an inline or described definition that fails its schema now answers 422validation-failedwith that message, where it answered 400Invalid request.
1.60.0
Minor Changes
-
d258795: A budget can cap what one person gives, not only what one member receives.
budget({ key, scope: "author", period, limit, cost })counts per tracker record author: the account that caused the record, such as a reaction's reactor, whether or not a member linked it. Put it in a tracker quest'sbudgetsbeside a"member"budget to cap both sides, for example a tip paid to a message's author withbeneficiary: "messageAuthor": at most 20 tips a day per receiver and 10 a day per reactor. A tip over either cap is rejected withbudget-exhausted, permanently; other reactors' tips keep paying. It works ontelegram.reactions(),discord.reactions(), kudos recipients and XinReplyToreplies. Author budgets share keys, calendar periods and the atomic commit with the other budgets, and imported completions don't count toward them.An author budget needs a tracker trigger.
domino checkreportsAUTHOR_BUDGET_TRACKER_REQUIREDon another quest,domino buildanddomino deployrefuse it, and Relay refuses its publication.The types SDK 1.x code reads are unchanged:
Budgetkeepsscope: "member" | "project", and a quest's description lists author budgets in the new optionalauthorBudgets(AuthorBudget,authorBudgetSchemafrom@domino-sdk/relay/schema), apart frombudgets.budget()returns anAuthorBudgetforscope: "author". A member's questbudgets(QuestBudgetView) leave author budgets out, since they cap someone else's giving. -
d41f5da:
relay.me.get()anduseMe()now return the member'sclock: { now, offsetMs }, the clock Relay judges their activity on. A test member's clock runs ahead after the staging toolbar's Next day ordomino test-members advance; every other member'soffsetMsis 0. NewmemberTime(me, at?)from@domino-sdk/relay/browserturns a real instant into the member's time, so an app that shows campaign phases or open quests by date follows an advanced test member.clockis optional, andmemberTimereturns real time when a Relay older than it omits it. On staging, the toolbar'sdomino:test-state-changedevent now carriesdetail: { change, actingAs }: what changed and which test member the page is signed in as. -
e899f47: Member imports carry referrals. A row's optional
referralnames the member's inviter (another row'sref, or the sign-in identity of an existing member), when they accepted, and the referral programs they already qualified in (qualified: [{ program, at }]). Applying records the referral and the qualifications without paying any referral reward, milestone or bonus; they count toward the inviter's qualified friends, and the friend's later points pay the program's bonus. An inviter's row applies before the rows that name it. New refusal codes:unknown-inviter,self-referral,inviter-already-accepted,referral-cycle,inviter-refusedandunknown-referral-program. The check summary addsreferrals(accepted, andqualifiedby program),management.referrals.participants()names the import that recorded a relationship or qualification inimport, andmanagement.members.get()returns the member's accepted referral in an optionalreferral(inviter,acceptedAt,suspended,import). -
8ee2000: A member import vouches for Telegram and Discord accounts too. A row's
accountstakes{ kind: "telegram", subject }and{ kind: "discord", subject }, each a numeric user ID, at most one of each per member, fromdomino importandmanagement.memberImports.addRows. They work like a vouched X account: Telegram and Discord tracker records, quest checks and Discord role checks trust them from when the import attached them, nobody else can link them, and the member's own Telegram or Discord link replaces them. They never sign in: a Telegram or Discord sign-in with one is refused withsign-in-method-disabled. A row naming a second account of a kind, or another one than the member's, is refused with the newMemberImportRefusalCode.TelegramAccountSelected(telegram-account-selected) orDiscordAccountSelected(discord-account-selected).@domino-sdk/relay/serveraddsmemberImportVouchedAccountSchema,memberImportFullRowSchemaandmemberImportFullRowsInputSchema, with the typesMemberImportVouchedAccount,MemberImportFullRow,MemberImportFullRowInputandMemberImportFullRowsInput, for rows with every kind of vouched account;addRowstakesMemberImportFullRowInput[].memberImportAccountSchema,memberImportRowSchemaandmemberImportRowsInputSchemastay the X-and-wallet form. Member identity states carryimported: truefor an X, Discord or Telegram account an import attached, and so does a connectedManagedTelegramAccount.An imported X, Telegram or Discord account now pays tracker records from the moment it was imported. Before, a vouched X account had no link time, so its records were skipped as historical. Members imported before this release keep that unknown link time until they link their X account themselves.
-
f0d5b8e: Offers can be archived and restored.
management.offers.archiveandunarchive(POST /management/v1/offers/OFFER/archive|unarchive),domino offers archive|unarchive OFFER --reason, Console Settings → Resources and the offer's card in Console Rewards archive one in test, and a promotion carries it to live. Each returns{ resource, changed }, typed by the newofferLifecycleResultSchema, withresource.kindoffer. An archived offer leaves member offer reads and refuses redemption withoffer-paused; management reads keep it, with the newarchivedAtfield onOffer, and its redemptions can still be canceled.management.resources.list()anddomino resources listlist offers in a newoffersarray (managedResourceListSchema), beside the existing kinds. A rollback now archives an offer the earlier promotion did not carry. -
6b5228b: Quests can announce a completion in a connected Telegram chat or Discord channel. List
telegram.postMessage({ chat, text, replyToTriggeringMessage })ordiscord.postMessage({ channel, text, replyToTriggeringMessage })in the new optionalannouncementsofdefineQuest, besiderewards(at most three; an array or a function of the settings). The text is a template with the placeholders{beneficiary},{author},{points},{rewards}and{quest}(announcementPlaceholders); people are mentioned through their linked account on that platform. Relay posts after the rewards commit, as a reward delivery with the newRewardDeliveryAction.PostMessageaction and an emptyroleId, retried with backoff and paced per chat; a failed post never undoes the rewards, andmanagement.retryRewardDelivery(id)retries one. Descriptions carry them in the new optionalannouncementsfield (announcementSchema).discord.channels({ postable: true })lists channels the bot can't post in as unavailable, andTelegramChatgains an optionalcanPost. The CLI's authoring reference documents announcements. -
ea1f033: Revert an applied or stopped member import:
management.memberImports.revert(id, { reason })starts it, anddomino imports revert IMPORT --reason TEXTstarts it and waits. In the background, one row at a time, it reverses the applied rows' opening balances (taking only points members still have;unreversedPointslists the rest), revokes their imported completions (reasonimport-reverted, freeing the occurrence) and removes the wallets, accounts and identities they linked unless the member signed in with one since (keptLinks). Members stay in the project. A reverting or reverted import reads asappliedwithrevertset, and is reverted once; the sameactionIdanswers the import again. Rows applied before imports recorded their links keep them (linksUntracked). Reverting needspublishpermission and works in live from API keys and CLI sessions too. Console shows Revert import on applied and stopped imports.MemberImportgainsrevert: the reason, who asked and when, rows reverted so far, thenrevertedAtand theoutcome. -
9a7ad11: Stop a member import while it applies:
management.memberImports.stop(id)anddomino imports stop IMPORTlet the row being applied finish, apply no more rows, and end the import: it reads asappliedwithappliedAtnull andstoppedAtandstoppedByset, and its remaining rows stayready. A stopped import can't be applied again; import the remaining rows in a new import. Stopping needspublishpermission and works in live from API keys and CLI sessions too. Console shows Stop import on an applying import.MemberImportgainsstoppedAtandstoppedBy; checkstoppedAtto tell a stopped import from a fully applied one. -
df3a08c: A test member's clock moves by hours and minutes, or to an instant, so time-of-day rules can be tested: block edges, or midnight in the project's time zone.
management.testMembers.advance(member, input)takes{ actionId }with one ofdays(1–30, unchanged: project-calendar days that keep the time of day),hours(1–720),minutes(1–43200) orto, an ISO 8601 instant with an offset that must be later than the member's clock (else 422test-member-clock-past). From the CLI,domino test-members advance MEMBERtakes one of--days,--hours,--minutesor--to. The 90-day limit holds across units (422test-member-clock-limit):daysstill add up to at most 90 and land no later than the 90th date, and hours, minutes andtonever move the clock past 90 days from now.offsetDaysstill counts the days advanced withdays; hours, minutes andtoleave it unchanged, so read the member's date fromtoday.@domino-sdk/relay/serverexportstestMemberClockAdvanceSchemaandTestMemberClockAdvanceInputfor every form;testMemberAdvanceSchemaandTestMemberAdvanceInputstay thedaysform.
1.59.0
Minor Changes
-
1d5a195:
management.attempts.acceptAnyway({ attempt, reason })(POST /attempts/<id>/accept-anyway,reviewpermission) accepts a rejected attempt anyway, for any member, reversing a reviewer's or an evaluator's rejection. The acceptance pays as a reviewer's approval would, with the normalcompletion.createdwebhook; the rejection stays in the attempt's events beside astaff-decisionwithdecision: "accept-anyway"and the reason, and the retry cooldown ends. Only the member's latest attempt on a quest that isn't archived qualifies (409attempt-superseded,quest-archived), and a cap the acceptance would break refuses it instead of rejecting it again (409completion-cooldown,budget-exhausted,completion-revoked,already-completed).management.attempts.getanswerssupersededBy, the member's newer attempt on the quest, or null. The CLI addsdomino attempts accept-anyway ATTEMPT --reason TEXT. -
a0c77e4: Archiving or unarchiving a group, pausing or resuming a tracker, and changing the project's schedule state take an optional reason, which the audit log records:
management.groups.archive(group, { reason })andunarchive(group, { reason }),management.trackers.pause(reference, { actionId, reason })andresume, andmanagement.schedules.setState(state, { reason }). A reason is a trimmed string of 1–500 characters, like the other lifecycle reasons; leaving it out works as before. The CLI adds--reason TEXTtodomino groups archive|unarchive,domino trackers pause|resumeanddomino schedules pause|archive|resume. Console asks for a reason in a dialog for each of these, and the project schedule menu no longer applies a state as soon as you pick it. -
424863f: Operators can restore an entitlement they voided.
management.entitlements.restore(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/restore,reviewpermission) anddomino entitlements restore <id> --revision <n> --reason <text>make avoidentitlementavailableagain at the tier an accepted completion still earns, and bring back any referral review the void settled. Restoring is refused with 409entitlement-statuswhen a completion revocation voided the entitlement or no accepted completion still earns it (the body names thecompletionto restore instead), when canceling an offer redemption voided it, or when a referral reward was voided before voids kept their reviews. Console Rewards and member detail offer Restore reward on a voided reward, and Audit history lists each restore with its reason. -
790aa5d: Operators can restore a completion they revoked by mistake.
management.completions.restore({ completion, reason })(POST /management/v1/completions/<id>/restore,reviewpermission) anddomino completions restore <completion> --reason <text>re-apply what the revocation recorded, not a fresh reward evaluation: the points it reversed are awarded again (points it could not reverse are not), its badges, group memberships, tiered rewards, Discord role grants, referral qualifications and streak milestone come back, and budgets stay spent. The result (CompletionRestoration) lists what came back. Restoring is refused with 409completion-not-revokedwhen the completion isn't revoked, and with 409occurrence-taken(naming thecompletionthat holds it) when the revocation freed the occurrence and the member completed it again.members.get(id).revokedCompletionslists up to 25 revoked completions, each with its revoke's reason and result. Console member detail and attempt detail offer Restore completion, and the attempt history and Audit history show the restore beside the revoke. -
850a252:
settlementPayoutCsv(exported)turns the result ofmanagement.leaderboards.settlementExport(id)into the payout list as CSV, with the columnsmember,score,rank,share,amount,unit,wallets.domino leaderboard-settlements exportnow prints it through this helper, unchanged, and Console's prize settlement panel offers Download payout list (CSV) for any pool that is not points, with the same content. -
7ac13ef: Solana transactions from
ctx.wallet.transactions()now carrysigner: whether the linked wallet signed the transaction. It isfalsefor a transfer someone else sent into the wallet, and for results captured before it existed. Check it before rewarding an action. The newsolana-actionsnippet completes a quest once per transaction a linked wallet signed that called your program, optionally receiving at least an amount of a token. -
5b94440: Anchor instruction decoding for
ctx.wallet.transactions(). Saving asettings.solanaProgram()setting now stores the program's Anchor IDL (legacy or 0.30+), read from its on-chain IDL account, in the published release's new optionalsolanaIdls(SolanaProgramIdl:{ program, source, idl }, by setting key). For a program without an on-chain IDL, an operator pastes one in Console or sends the new optionalsolanaIdls: { [settingKey]: idl | null }withquests.updateSettings()(anchorIdlSchema,AnchorIdl). With an IDL, each transaction gains optionalinstructions: the program's top-level and inner instructions in call order, decoded as{ name, args, accounts }(WalletInstruction). An instruction the IDL does not describe is skipped. The newinstruction(tx, name)helper in@domino-sdk/relay/authoringreturns the first matching instruction's args, ornull. The testing kit'sfacts.wallet.transactionsacceptsinstructions. -
95cb8d8: On-chain action checks on Solana.
ctx.wallet.transactions({ program, since, afterActivation?, mint?, address? })reads the successful, finalized transactions of the member's linked Solana wallets sincesincethat invokedprogramin a top-level or inner instruction, oldest first. It returns{ program, chain, mint, since, at, complete, transactions: [{ wallet, signature, slot, timestamp, programs, balanceChanges: [{ mint, delta }] }], observedAt }(WalletTransactions,WalletTransaction,WalletBalanceChange,walletTransactionsSchema), withmint"SOL"for lamports (rent included, the fee the wallet paid left out) anddeltaa signed integer string, or null when the member has no linked Solana wallet, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules. Withmint, only each wallet's associated token account for that mint is read.sincemay be at most 30 days before the evaluation (maxWalletTransactionLookback); each wallet's read fetches at most 1,000 transactions (maxWalletTransactions), and a wallet with more returns its newest ones withcomplete: falseinstead of throwing.WalletReader.transactionsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The program comes from the new
settings.solanaProgram({ label, default })setting, a base58 address (SolanaProgram,isSolanaProgram) stored as a text setting withpicker: "solana-program". Publication, Console settings edits and unarchiving check that the account exists on Solana mainnet and is executable.New pure helpers in
@domino-sdk/relay/authoringread one transaction:received(tx, mint)andspent(tx, mint)return the wallet's gain or loss as an integer string, or"0";swapped(tx, { into?, from? })checks that one mint went down and a different one went up;invoked(tx, program). The testing kit answersctx.wallet.transactions()fromfacts.wallet.transactions.
Patch Changes
-
aaa07ad: The SDK and CLI depend on Zod 4.6.5, whose schemas keep their methods on a shared prototype instead of binding about 7 KB of them to every schema instance. Importing the SDK takes far less memory: a Relay backend process that loads the server entry starts with about half the heap. Quest
settings.datetime()values, member-import times and X postcreatedAtvalues (including hand-written testing fixtures) still accept minute-precision instants such as2026-11-01T00:00Z, which Zod 4.6 alone rejects. An X post read without amediafield still gives an empty list.One tracker data schema change after a rebuild: a field declared as
z.number().min(0).int()now storesminimum: 0, so Relay refuses negative values at ingestion that the app's own schema already refused. Zod 4.3 stored the safe-integer minimum for that order of checks.
1.58.0
Not published to npm. Its changes first reached npm in 1.59.0.
Minor Changes
- 321763b:
management.discordRoles.list()lists every Discord role the project's reward deliveries have granted in the environment, with the quests that requested it and its deliveries counted by status (pending,delivered,failed). It reads the whole delivery history, so a role granted only before the newest page ofmanagement.rewardDeliveriesstill appears. The response schema isdiscordRoleRewardsSchema.
Patch Changes
- a2318dd:
stagingPathSchemarefuses paths with control characters and accepts only a path that resolves on the staging host. Browsers drop tab and newline from a URL, so a path such as/<tab>/example.comused to pass and open another site after a staging sign-in.
1.57.0
Not published to npm. Its changes first reached npm in 1.59.0.
Minor Changes
-
7012d1c: Contract state and Cosmos staking reads in quest evaluators.
ctx.wallet.call({ call, args?, address? })calls onevieworpureEVM contract function once per linked EVM wallet, every call at one block, through dRPC on the chainsmanagement.walletBalanceChains()lists: state a contract keeps per address, such asstakedOf(address),getVotes(address)oruserInfo(uint256, address).{ wallet: true }inargsstands for each wallet's address. It returns{ call, chain, contract, returns, total, wallets: [{ address, value }], at, observedAt }(WalletCall,walletCallSchema), with integers as decimal strings andtotaltheir sum (null forboolandaddressreturns), or null when the member has no linked EVM wallet. A revert is anauthor-errorcarrying the contract's reason. The call comes from the newsettings.contractCall({ label, default })setting (picker: "contract-call"): the contract's CAIP-10 ID and a human-readable signature, such aseip155:8453:0x… function stakedOf(address) view returns (uint256)(ContractCall,isContractCall,parseContractCall,parseContractFunction). Onlyvieworpurefunctions with elementary parameters that return oneuint,int,booloraddressare accepted; publication, Console settings edits and unarchiving also check the chain.ctx.wallet.delegations({ chain, validator?, address? })reads the native staking delegations of the member's wallets linked on a Cosmos SDK chain Relay lists, at one block, in base units of the chain's staking denomination:{ chain, denom, validator, total, wallets, validators, at, observedAt }(WalletDelegations,walletDelegationsSchema), optionally only to one validator, or null when the member has no wallet on that chain.Both follow
ctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.WalletReader.callandWalletReader.delegationsare optional in the type so that existing test doubles compile; Relay supplies them to every evaluation, and the testing kit answers them fromfacts.wallet.callandfacts.wallet.delegations. Newcontract-stateandcosmos-stakersnippets accept members whose view call reaches a minimum, or who delegate at least a minimum, optionally to a configured validator. -
59a01d8: On-chain action checks on EVM chains.
ctx.wallet.events({ event, account, since, afterActivation?, where?, address? })reads the events of one contract whose indexed address argumentaccountis one of the member's linked EVM wallets, fromsinceto the chain's finalized block (its safe block on a chain without finality), through dRPCeth_getLogson the chainsmanagement.walletBalanceChains()lists. It returns{ event, chain, contract, since, at, finality, logs: [{ wallet, block, timestamp, tx, logIndex, args }], observedAt }(WalletEvents,walletEventsSchema) with decoded arguments, or null when the member has no linked EVM wallet, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.whereholds equality or{ gte }conditions on other arguments (WalletEventCondition);afterActivationskips events before the quest's activation.sincemay be at most 30 days before the evaluation (maxWalletEventLookback), and a read over more than 3,000,000 blocks or 1,000 logs (maxWalletEventLogs) throwsauthor-error.WalletReader.eventsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The event comes from the new
settings.contractEvent({ label, default })setting: a CAIP-10 contract, a space and the event's Solidity signature with argument names andindexedmarkers, such aseip155:8453:0x… Transfer(address indexed from, address indexed to, uint256 value)(ContractEvent,isContractEvent,parseContractEvent,ContractEventParts), stored as a text setting withpicker: "contract-event". An event without an indexed address argument is refused. Publication, Console settings edits and unarchiving also check that the chain reports a finalized or safe block and that the address holds a contract.The testing kit answers
ctx.wallet.events()fromfacts.wallet.events. A newonchain-actionsnippet rewards each new matching event since the later of the quest's activation and the wallet's link, with an optional minimum amount, keyed onchain:tx:logIndex. -
956b721: NFT holding checks on EVM chains and Solana.
ctx.wallet.nfts({ collection, address? })counts the NFTs of one collection the member's linked wallets of its family hold: on EVM, an ERC-721 contract'sbalanceOf(owner), or one ERC-1155 token ID'sbalanceOf(owner, id), through dRPC on the chainsmanagement.walletBalanceChains()lists; on Solana mainnet, the NFTs whose Metaplex metadata names the collection withverified: true(SPL Token and Token-2022 accounts holding exactly 1 of a 0-decimal, supply-1 mint; compressed NFTs and Metaplex Core assets are not counted). It returns{ collection, chain, standard, total, wallets: [{ address, count }], at, observedAt }(WalletNfts,walletNftsSchema;standardis"erc721","erc1155"or"metaplex"), or null when the member has no linked wallet of that family, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.WalletReader.nftsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The collection comes from the new
settings.nft({ label, default })setting, a CAIP-19 asset type such aseip155:1/erc721:0x…,eip155:8453/erc1155:0x…/1orsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/nft:<collection mint>(NftCollection,isNftCollection,parseNftCollection,NftCollectionParts), stored as a text setting withpicker: "nft". Publication and Console settings edits check it: an EVM chain and that the contract reports the standard through ERC-165supportsInterface, so a token contract or the wrong standard is refused when it is set; a Solana mint must be a Metaplex collection NFT, so a fungible mint, a mint without Metaplex metadata, a print edition or a member NFT is refused. Unarchiving checks it too, and a platform without its dRPC key refuses every EVM collection.ctx.wallet.balance()pointed at an ERC-721 or ERC-1155 contract now throwsauthor-errornamingctx.wallet.nfts()instead of reading a token count; it asks ERC-165 alongsidedecimals()on every token read.The testing kit answers
ctx.wallet.nfts()fromfacts.wallet.nfts. A newnft-holdersnippet accepts members whose linked wallets hold at least one NFT from the configured collection, on EVM or Solana. -
1770ada: A seventh starter,
loyalty: members join a campaign, earn points from its steps, join a members' group through a quest's group membership reward, and spend points on offers, one of them limited to the group withinGroup. Console offers it anddomino create --template loyaltycreates it.Starters are now an open list, so adding one no longer changes the SDK's types.
starterslists every starter as aStarterwith a plain stringid,offeredStarterSchemachecks an ID against it, andprojectCreateInputSchemaandrepositoryCreateSchemaaccept any offered starter;management.createProjectuses the first.starterTemplates,StarterTemplate,StarterId,starterIdSchema,projectInputSchemaandcreateRepositorySchemaare deprecated: they keep the first six starters and are removed in 2.0.0. To upgrade, replacestarterTemplateswithstartersandstarterIdSchemawithofferedStarterSchema, and type a starter ID asstring. -
cec850f: Create and change a tracker of every managed source without a deployment, not only X search.
management.trackers.createandmanagement.trackers.update(POST /trackers,POST /trackers/:tracker/update) take asource:{ kind: "x-search", query },{ kind: "discord-messages", text? },{ kind: "discord-reactions" },{ kind: "telegram-messages" },{ kind: "telegram-reactions" },{ kind: "youtube-shorts" }or{ kind: "tiktok-videos" }. Each is stored as the source its builder makes, such astelegram.reactions(), so quests read a data tracker as they read the same tracker in code.queryalone still creates an X search tracker; create takes exactly one ofqueryorsource. An update'ssourcereplaces the source's settings but never its kind, which is refused with 422validation-failed. Turning Discord message text off erases the text already stored.managedTrackerCreateSchemavalidates the new input, whiletrackerCreateSchemakeeps its X-only shape.managedTrackerSource(source)reads a published source by these names, andmanagedTrackerSourceKindslists them.domino trackers create KEY --source <kind>creates one from the CLI, with--text onfor Discord messages;domino trackers update TRACKER --text on|offturns message text on or off. Console Trackers → New tracker offers every source. -
94726d8: Organization automation keys (
dak_): a credential for CI and headless coding agents that no person owns. An organization administrator creates one in Console Settings → API keys for every project or a chosen list, with chosen permissions and an optional permission to create projects. It works asRELAY_MANAGEMENT_TOKEN, withdomino login --token-stdinand the Git credential helper, in the test environment only: live writes, administration and minting other credentials are refused.The management client adds
automationKeys(list,create,addSecret,revokeSecret,revoke) withautomationKeyInputSchema,automationKeySchema,issuedAutomationKeySchemaandautomationKeyPermissionSchema.accessSchemagains an optionalcredentialthat/accessanddomino whoami --jsonfill for an automation key with its kind and scope. -
6d9f0de:
telegram.messages()is a tracker source of the messages members post in the project's connected Telegram groups and supergroups, one record per message with the poster's Telegram account asauthor.dataisTelegramMessageData:chatId,chatType,messageId,replyToMessageIdandthreadId, never the text. Bots', service and channel messages are not recorded. In SDK 1.x it is a custom source on the reserved namespacetelegram-messages, which Relay alone writes. The newtelegram-contributionsnippet rewards posting once per member per day. -
8d11f64:
wallet.balances({ chain, token })is a tracker source of daily balance samples that Relay writes itself. Once per project-calendar day, at a time members can't predict, Relay reads one token's balance in every wallet linked by a member who joined the project, every wallet of a run at one pinned EVM block or Cosmos height (on Solana, every read must reach the run's pinned slot).chainandtokenare those ofctx.wallet.balance(). Each record is one wallet, keyed by its CAIP-10 account on the sampled chain, which is also itsauthor; each sample is a new observation whoseoccurredAtis the sample time and whosedataisWalletBalanceSample(amountin base units,decimals,block). A provider outage retries the same block later that day and never writes a zero, so a day with no successful sample breaks astreak({ days }). Reward holding for days withautomatic({ tracker, where: { path: "amount", gte: { setting: "threshold" } } })andstreak({ days }). In SDK 1.x it is a custom source on the reserved namespacewallet-balances, with the chain and token in the new optionalbalancefield of custom sources (walletBalanceSourceSchema); ingestion refuses the namespace, and a project and environment can have at most 5 such trackers (limit-reached).management.trackers.createand Console New tracker take{ kind: "wallet-balances", chain, token }(managedTrackerSourceSchema), anddomino trackers create --source wallet-balancestakes--chainand--token. The newtoken-hold-streaksnippet rewards holding a token for 7 days in a row.
1.56.0
Minor Changes
- 9ec9f2f: Publishing creates the groups code names. A deployment whose quest
groupMembership(group)reward orinGroup(group)prerequisite, offerinGroup(group)requirement, leaderboardgroupor referralinviterGroupnames a group the environment doesn't have now creates it, with no members and thedefineGrouphandle's title (the key when only a leaderboard or referral program names it), instead of refusing with 422validation-failed. Approving a promotion creates the groups live lacks the same way. The deployment preview and result, and the prepared promotion, list each such group as agroup-createdwarning, and the audit history recordsgroup-createdwith the deployment or promotion as its reason. An existing group keeps its title and members, and publishing never unarchives one: agroupMembershipreward naming an archived group still refuses. A project whose code uses groups no longer needsdomino groups createbefore its first deployment or promotion. - fbe8e64: Offers take prerequisites.
defineOffer({ ..., requires })accepts the prerequisites quests do,completed(quest),balanceAtLeast(ledger, minimum)andinGroup(group), at most 30;offerDefinitionSchemaandofferAuthoringSchemagain an optionalrequires. Publication refuses a required quest or ledger the deployment doesn't know. Redeeming as a member who misses one fails with 409prerequisites-incompleteandmissing, the shape quest admission uses, before any points or stock are spent.Offergainsrequiresandmissing: for a signed-in member,relay.offers.list()anduseOffers()list the prerequisites they have not met, empty for a read without a member or an offer withoutrequires.availabilitykeeps its four kinds. Console Rewards shows an offer's requirements on its card, and a promotion carries them to live. - cba70f4: Add the organization's billing read.
management.billing.organization()anddomino billing organization(GET /management/v1/organization/billing) are open to any member of the organization and need no project. They list the projects the caller may read, each with its plan,liveandliveStateasmanagement.usage.plan()reports them, and its Stripe subscription. They also return the organization's Free projects against their limit (freeProjects), the free AI allowance its Free projects share (freeAi), the monthly AI allowance across the organization, the trial, whether plan limits are enforced, and whether the caller may open the customer portal (billing).management.billing.organizationPortal()(POST /management/v1/organization/billing/portal) opens the customer portal asmanagement.billing.portal()does, returning to Console's organization billing page.organizationBillingSchemaand theOrganizationBillingtype are exported from@domino-sdk/relay/server, with a reader inmanagementResponses. - 6458ebe: Add the project's plan read.
management.usage.plan()anddomino usage plan(GET /management/v1/usage/plan,readpermission) return the project's plan and where it came from (source), whether it may serve live and why (live), live's notice or offline state (liveState), the organization's one live trial (trial), whether the plan includes a custom domain, the AI builder balance by pool with when it renews (ai), the Stripe subscription asmanagement.billing.read()reports it, and whether plan limits are enforced. Unlikemanagement.billing, it is open to anyone who can read the project.projectPlanSchemaand theProjectPlantype are exported from@domino-sdk/relay/server, with a reader inmanagementResponses. - 62c3791: Add TikTok creator accounts and the
tiktok.videos()tracker source. A signed-in member links their TikTok account through TikTok's consent screen withrelay.auth.creatorAccounts.connect("tiktok")(useCreatorAccounts()in React); the account's subject is itsopen_id, its profile a display name and avatar, andknownCreatorAccountProvidersnow lists"tiktok". Operators read and unlink it like a YouTube channel.tiktok.videos()from@domino-sdk/relay/authoringreads the public videos on the accounts members linked, only with each member's own token, everyrefreshSecondsof the tracker, as content with aTikTokVideoBody(title,description,durationSeconds,width,height,createdAt,shareUrl,isAigc) and the metricsviews,likes,commentsandshares, socontent()quests,performanceand engagement rules use it. A known video the account no longer returns is a content removal; an account whose token needs reconnecting is not read, asprovider-unavailable. It is a custom source on the reserved namespacetiktok-videos, which ingestion refuses. Both are inert until Domino configures TikTok. Thetiktok-videoscapability and the participant and authoring references describe them. - a533f93: Add creator accounts and the
youtube.shorts()tracker source. A signed-in member links the YouTube channel they publish from as a creator account:relay.auth.creatorAccounts.list()(GET /v1/auth/creator-accounts),connect(provider)anddisconnect(provider), anduseCreatorAccounts()in React. A creator account is never a sign-in method, linking another channel replaces the current one, and the account's subject is the channel ID picked on Google's consent screen. Each entry mirrorsAccountConnectionwith the channel'sprofile;provideris an open string, withknownCreatorAccountProvidersin@domino-sdk/relay/schemalisting"youtube", soCreatorAccount,CreatorAccountsand their schemas sit beside SDK 1.x's closed provider lists. Operators read them onmanagement.members.get(member).creatorAccountsandmanagement.members.creatorAccounts(member), andmanagement.members.unlinkCreatorAccount(member, { provider, subject, revision, reason })unlinks one withreviewpermission;managedCreatorAccountSchema,managedCreatorAccountsSchemaandmemberCreatorAccountUnlinkInputSchemaare exported from@domino-sdk/relay/server.youtube.shorts()from@domino-sdk/relay/authoringreads the Shorts on the channels members linked, everyrefreshSecondsof the tracker, as content with aYouTubeShortBody(title,description,tags,durationSeconds,publishedAt) and the metricsviews,likesandcomments, socontent()quests,performanceand engagement rules use it. It is a custom source on the reserved namespaceyoutube-shorts, which ingestion refuses. Both are inert until Domino configures YouTube. Thecreator-accountsandyoutube-shortscapabilities and the participant and authoring references describe them.
1.55.0
Minor Changes
-
5c70548: Add monthly active member usage.
management.usage.activeMembers()anddomino usage active-members(GET /management/v1/usage/active-members,readpermission) return the project's live active members this UTC month (month,activeMembers), its plan and the plan'slimit(null when the plan has none), and the six earlier months inhistory, newest first. An active member was active in the month on a UTC day after the day they joined the project: a signed-in participant API request, which includes their attempts and redemptions. Imports, server-reported tracker records and test members never count.activeMemberUsageSchemaandactiveMemberMonthSchemaare exported from@domino-sdk/relay/server, with a reader inmanagementResponses. -
65f3a9a: Add four metrics to the analytics catalog.
groups.joinsandgroups.leavescount members added to and removed from a group, by when the change happened, without repeats that left membership as it was or memberships recorded without a date; theirgroupbreakdown and filter mean the change's group.trackers.recordscounts tracker trigger outcomes by when the record was observed, as each outcome is now, with anoutcome(admitted,skipped) ortrackerbreakdown and atrackerfilter that keeps the tracker's own outcomes.leaderboards.settledParticipantscounts the rows of pending and approved leaderboard settlements by each settlement's cutoff, with aleaderboardbreakdown and filter, and draws asnumber,barortable.analyticsBreakdownsgainsgroup,tracker,outcomeandleaderboard, andanalyticsFiltersgainsleaderboard. A dashboard panel naming a leaderboard the environment doesn't have fails the deployment withunknown-reference. -
8f12977: Add the closed metric catalog.
management.analytics.catalog()anddomino analytics metrics(GET /management/v1/analytics/catalog) list every metric with its definition, kind (count,distinctorrate), the breakdowns and filters it accepts, its allowed charts and whether it needs a point ledger;analyticsCatalogand theAnalyticsMetricId,AnalyticsBreakdown,AnalyticsFilterandAnalyticsCharttypes are exported from@domino-sdk/relay/server.management.analytics.query({ metric, range, breakdown?, filter?, ledger?, limit? })anddomino analytics query --metric ID [--range] [--breakdown] [--filter name=a,b]... [--ledger] [--limit](GET /management/v1/analytics/query,readpermission) read one metric bucketed likeanalytics.read, and with no breakdown or filter return the same numbers. A breakdown by quest, offer, referral program, rejection source or rejection reason has a row per matching resource (archived ones only with activity), sorted by total and capped bylimit(1 to 100, default 100) with the rest summed intoother. Filters narrow to quests, offers or programs by key, the quests a tracker triggers, or members now in a group. The new ratesquests.rejectionRateandreferrals.qualificationRatearenullbelow 10 in the denominator. A query the catalog doesn't accept is a 422 withunknown-metric,breakdown-not-accepted,filter-not-accepted,ledger-required,ledger-not-accepted,invalid-limitorunknown-reference, and a missing group isgroup-not-found.analyticsCatalogSchema,analyticsMetricQuerySchemaandanalyticsMetricResultSchemaare exported, with readers inmanagementResponses. -
c91ba35: A repeatable quest can space out a member's completions. Set
completionCooldownMs(1,000 to 604,800,000) on a quest members submit that completes more than once, or Completion cooldown on a quest made in Console. After a completion of any key is accepted, a new attempt is refused with 409completion-cooldown(RelayErrorCode.CompletionCooldown) untilretryAt; when the retry cooldown also runs, the later one is named. A quest that completes once refuses the field. Quest progress andQuestViewcarrynextAvailableAt, when the member may next submit: the latest of the retry cooldown's end, the completion cooldown's end and an exhausted budget's next reset, omitted when nothing will reopen the quest.questStatusreportscompletion-cooldown,canSubmitis false until then, anduseQuestrenders again when it ends. Catalog quest instances take an optionalcompletionCooldownMs(nullfor none). A quest without the field keeps today's behavior. -
6b946d8: Add dashboards: a
relay.jsondefinition kind built from the metric catalog. Default-exportdefineDashboard({ key, title, description?, defaultRange?, position?, sections })from@domino-sdk/relay/authoring, typed so each panel accepts only the breakdowns, filters and charts its metric allows, and list it underdashboards: [{ entry, exportName? }]in relay.json. A deployment refuses a dashboard with a coded 422 (unknown-metric,breakdown-not-accepted,breakdown-required,filter-not-accepted,chart-not-accepted,ledger-required,ledger-not-accepted,unknown-reference,referral-program-required,duplicate-key,too-many-panelsortoo-many-dashboards), publishes a new version only when the definition changed, and reports each one in the preview'sdashboardsascreated,updatedorunchanged.management.dashboards.list({ includeArchived? })anddomino dashboards list(GET /management/v1/dashboards) list them;management.dashboards.read({ key, range? })anddomino dashboards get KEY [--range](GET /management/v1/dashboards/KEY) return the definition and every panel'sanalytics.queryresult, with a failing panel's ownerror(group-not-found,panel-timeout) beside the others.management.dashboards.archive|unarchive(key, { reason })anddomino dashboards archive|unarchive KEY --reason(POST /management/v1/dashboards/KEY/archive|unarchive) archive and restore one in test, returning{ dashboard, changed }, and promotions carry dashboards to live by key as changes of kinddashboard. The deployment preview'sdashboardsis optional, so values built for older SDKs still fit.dashboardDefinitionSchema,dashboardAuthoringSchema,normalizeDashboard,dashboardIssueanddashboardPreviewSchemaare exported from@domino-sdk/relay/schema, anddashboardListSchema,dashboardReadSchemaanddashboardLifecycleResultSchemafrom@domino-sdk/relay/server, with readers inmanagementResponses. -
3a8a888: Engagement points land in the leaderboard window they were measured in. Each engagement settlement's point entries now take effect when the content sample that measured their growth was observed, instead of when the daily settlement paid them, and budgets charge each entry to the period of its
effectiveAt. When the growth between two samples spans a leaderboard window or budget period boundary, Relay splits it there in proportion to time (rounded down, the remainder to the later part), one entry per side. Before, every earned board over engagement points ran about a day late.A settling leaderboard whose score counts an engagement rule's points now takes a closing measurement before it freezes. While it closes, its settlement reads as
pendingwith no rows in the review reads (settlementReviews,settlementReview), with a new optionalclosingfield holdingstartedAtanddeadline: Relay asks scheduled trackers to resample the content, a sample observed within 30 minutes after the cutoff counts as measured at it, and the settlement freezes when everything is measured or the wait ends. Settlements gainunmeasured: content frozen at its last good sample, withcontent,memberandobservedAt.management.leaderboards.retrySettlementMeasurement(id, { actionId? })anddomino leaderboard-settlements retry-measurement SETTLEMENT(POST /management/v1/leaderboard-settlements/:id/retry-measurement,reviewpermission) close such a settlement again for another 30 minutes. Approving a closing settlement fails withsettlement-closing; retrying one with nothing unmeasured fails withsettlement-measured. The oldersettlementsandsettlementreads omit closing settlements. -
5ae31db: Evaluators read the member's groups, campaign enrollment and referral progress.
ctx.member.groups()returns the keys of the project's unarchived groups the member belongs to.ctx.member.enrolled(campaign)takes a campaign definition or key and returns whether the member's enrollment is active:falseafter withdrawal and without one.ctx.member.referrals(program)takes a referral program handle, key or ID and returns the member's progress as inviter, asrelay.profiles.referral()returns it, never codes or accounts. Each is pinned once per evaluation run at its first call and counts against the run's capability calls; an unknown campaign or program fails withauthor-error.ReferralHandlenames the handledefineReferralreturns.The testing kit answers them from
facts.member.groups,facts.member.campaigns({ key, enrolled }per campaign) andfacts.member.referrals. Like the other member-state calls, they are not listed in a run'scalls. -
d463895: Evaluators read the member's point balances and quest completions.
ctx.member.balance(ledger)takes a ledger handle or key and returns the member's balance in it, asrelay.me.balances()lists it.ctx.member.completions({ quest?, since?, limit? })returns the member's accepted, unrevoked quest completions in the project, newest first, each with the quest key, completion key andcompletedAt;limitdefaults to and is capped at 100. Both are pinned once per evaluation run at their first call, count against the run's capability calls, and fail withauthor-errorfor an unknown ledger or quest. Write rules such as "2 of these 5 quests" or "a balance of 100 or one of these quests" inevaluatewith them.EvaluationMemberis now@compat sealed: Relay and the testing kit create it, and a 1.x minor release can add members to it.The testing kit answers the readers from
facts.member.balancesandfacts.member.completions, filtering and ordering completions as Relay does. Like Telegram calls, their calls take sequence numbers but are not listed in a run'scalls. -
7ff5984: Evaluator failures are coded. A quest bundle that throws while loading, an
evaluatethat throws anything without an escaped capability failure in it, and one that returns no decision now fail the attempt once with codeevaluator-errorand the thrown error's name and message (at most 1,000 characters), instead of retrying and ending with "Execution failed after retries". A retryable capability failure that escapes, rethrown or wrapped as acause, is still retried.An evaluation run's ninth capability call throws
CapabilityErrorwith the new codecapability-limit, never retryable, without reaching Relay; before, quest code saw an opaque failure. Likeintegration-required, it is a codecapabilityErrorCodesdoes not list.reservedAttemptCodesnow includesevaluator-errorandcapability-limit, so a reject code cannot use them.The testing kit reports both the same way: the ninth call throws the
capability-limitCapabilityErrorinstead of failing the run with aTestSetupError, andrunQuestrejects with the newEvaluatorError(codeevaluator-error, the same message, and what quest code threw ascause) for anything quest code throws other than a capability failure it received. A test that expected the thrown error itself reads it fromcause.maxCapabilityCallsis exported from@domino-sdk/relay/testing. -
6cc08fa: A member can greet themselves by first name. Each entry of
relay.profiles.mine().optionsgainsgivenName: the first name Google sent at sign-in (OIDCgiven_name), null for X, Discord and accounts without one. Older servers' answers still parse, withgivenName: null. Only the member's own profile carries it; profile reads by ID and leaderboard rows don't. Existing Google accounts get it on their next sign-in. Operator member reads are unchanged. -
921e9d7: Add
dominoHosting({ command })to@domino-sdk/relay/vite. It returnscloudflare, options for the app's owncloudflare()call from@cloudflare/vite-plugin(the Worker in Vite'sserverenvironment, and, only when serving, the variablesdomino devsets forwarded to the local Worker, empty when unset), andserver, Vite's server settings (port fromDOMINO_APP_PORT, 5173 by default, and theDOMINO_PREVIEW_ORIGINhost with HTTPS hot reload when it is set). The SDK does not depend on the Cloudflare plugin.Add
domino build --hosting-artifacts, which writes the quest and catalog artifacts the hosting builder reads to.domino-build/quests.jsonand.domino-build/catalog.jsonbesiderelay.json.The starters use both: their
vite.config.tsis their own plugins plusdominoHosting(), andscripts/build-domino.mjsgives way tovite build && domino build --hosting-artifacts. Existing apps keep working unchanged. -
6156dec: Automatic attempts that failed with
integration-requiredare admitted again. When a project's Discord connection becomes ready, on first connection or after a repair from needs attention, Relay admits a new attempt in the background for each member of an active automatic quest without a tracker that needs Discord whose latest attempt failed that way.isRetryableAutomaticOutcomenow also returnstruefor an outcome whose linked attempt isfailedwith codeintegration-required, so automatic catch-up (management.quests.previewAutomaticCatchUpandcatchUpAutomatic) accepts these outcomes too. Its signature is unchanged. Console marks published quests that need Discord while it is missing, unavailable or needs attention. -
a801a1d: Leaderboard settlements can split a pool prize by score.
settle.poolis{ amount, unit, ledger?, minScore? }:amountis the whole pool in base units as a decimal string, andunitis"points"or{ code, decimals }for a token or currency. A points pool names theledgerit pays into (a ledger handle or key indefineLeaderboard) and holds at most 2,147,483,647 points; other units take no ledger. With a pool,settle.prizesmay be empty (defineLeaderboardlets you leave it out), and a board can have both. When the window freezes, Relay splits the pool over the eligible frozen rows in proportion to score, in exact integer math: members scoring 0 or less, or at or belowminScore, get nothing, each pool share rounds down, and the leftover base units go one each to the largest remainders, earlier positions first, so the shares sum exactly to the pool. With no eligible score the whole pool stays unallocated.Settlement reads (
settlement,settlementReview) gainpool, withallocatedandunallocated, and each row gainssharein base units; both are absent from settlements without a pool. Approval pays a points pool's shares as points in each member's prize completion, and revoking the completion reverses them. Relay pays no other unit:management.leaderboards.settlementExport(id)(GET /management/v1/leaderboard-settlements/:id/export,readpermission) anddomino leaderboard-settlements export SETTLEMENT(CSV, or--json) list each member's score, rank, share in base units and whole units, and verified linked wallets, as the operator's payout list. Console shows each winner's pool share before approval. -
cbdcdc0: Add the plan limits in test and setup, where plans apply to the organization. A sign-in that would add a real person to a test environment that already has its plan's number of real people fails with
limit-reached(403,limit: "real-people-in-test",count,maximum); people who already joined, test members and live sign-ins are unaffected. Creating a project, or transferring a Free project in, fails withlimit-reached(403,limit: "free-projects") when the organization already holds 10 Free projects. Adding a custom domain needs the Growth plan or above and otherwise fails withplan-required(403,feature: "custom-domain").management.usage.realPeopleInTest()anddomino usage real-people-in-test(GET /management/v1/usage/real-people-in-test,readpermission) return the test environment'srealPeople, the project'splan, itslimitand whether it isenforced;realPeopleInTestUsageSchemais exported from@domino-sdk/relay/server, with a reader inmanagementResponses. The CLI's error JSON keeps alimit-reachedorplan-requiredrefusal's detail indetails. -
823a691: Member accounts that Relay reads in the background with the member's own OAuth tokens now report when those tokens stop working. In management member reads (
management.members.get,list,lookup, and thepersonon attempts) such an account carriesstatus: "connected", orstatus: "needs-reconnect"with areasonofrevoked,expiredormissing-scope; accounts Relay keeps no tokens for carry neither field.domino members getand the other CLI results include the same optional fields. The participant client'srelay.auth.accounts()anduseAccounts()report the account'sstatusas"reconnect"until the member links it again. Relay refreshes kept tokens before they expire. No provider reads accounts in the background yet. -
cc459ef: Add
questActionState(quest)anduseQuestAction(view)to@domino-sdk/relay/react.questActionStatetakes auseQuest()result and returnsopen(show the quest's input: the participant can submit now, or is starting a new submission after discarding a failed one on an active quest),counted(the submission just sent was accepted on a repeatable quest) andautomatic(the server owns every run).useQuestAction(view)returnsuseQuest(view)with those three fields added.Add
signedInMember(request, env, { apiUrl })to@domino-sdk/relay/portal-proxy. A hosted app's server route calls it to learn the signed-in member's ID, or null for a visitor or a session that is not a member's. It asks Domino's/v1/auth/sessionthrough the app's participant proxy with the request's cookies.The starters use
questActionStateinstead of their own copies of these rules, and the classic starter'sserver/participant.tsgives way tosignedInMember. -
a4c93bc: Add Stripe billing for a project.
management.billing.checkout({ plan, interval })anddomino billing checkout --plan launch|growth|scale [--interval month|year](POST /management/v1/billing/checkout) return a Stripe Checkout URL that subscribes the project;management.billing.portal()anddomino billing portal(POST /management/v1/billing/portal) return the organization's Stripe customer portal. Both need an organization administrator, not an API key (403permission-required). While billing is staff-only, every organization but Domino's own is refused with 403billing-staff-only. A project whose subscription holds its plan is refused another checkout with 409subscription-exists, and the portal answers 409no-customerbefore the organization's first checkout; a deployment without Stripe answers 503not-configured.management.billing.read()anddomino billing show(GET /management/v1/billing,readpermission) return whether billing is open to the caller (available,reason) and, when it is, the project'ssubscription:plan,interval, Stripe'sstatus,currentPeriodEnd,cancelAtPeriodEnd,lastInvoiceandlivemode.billingCheckoutInputSchema,billingSessionSchema,billingSubscriptionSchemaandprojectBillingSchemaare exported from@domino-sdk/relay/server, with readers inmanagementResponses. -
b8348fd: Change a project's Stripe plan, cancel and resume.
management.billing.changePlan({ plan, interval })anddomino billing change --plan launch|growth|scale [--interval month|year](POST /management/v1/billing/plan) move a paying project's subscription: a higher plan or annual billing applies now with the prorated difference invoiced at once, and a lower plan or monthly billing is held for the renewal. The answer isbillingPlanChangeSchema:timing(now,renewalornone) andinvoiceUrl, set when the immediate charge needs the customer and nothing changes until it is paid.management.billing.cancel()andmanagement.billing.resume()(domino billing cancel,domino billing resume;POST /management/v1/billing/cancel|resume) set the subscription to end when its period does, or take that back, and return the project's billing. A project without a subscription that holds its plan is refused with 409no-subscription, and a plan change while the subscription is set to cancel with 409subscription-canceling. The subscription now carriesscheduledChange, the plan, interval and date of a change held for the renewal, or null. The customer portal no longer changes or cancels plans; it keeps cards, invoices and billing details. -
c055c43: A project's Stripe subscription follows the project. Deleting a paying project sets its subscription to end with its period, and restoring it resumes the subscription; purging ends it at once. A transferred project keeps its plan until the old organization's period ends, and
management.billing.checkout()(domino billing checkout) subscribes it in the receiving organization with no charge before then. The subscription now carriesendedFor:project-deleted,project-transferredor null. -
f89ea5d: Buy AI builder credit, and see an overdue subscription.
management.billing.topup({ quantity })anddomino billing topup [--quantity 1-10](POST /management/v1/billing/topup) return a Stripe Checkout URL buyingquantitypacks of $10 AI builder credit for the project, on any plan; the credit is added once the payment is in, once per purchase.billingTopupInputSchemaandbillingTopupMaxQuantityare exported from@domino-sdk/relay/server. The subscription now carriesunpaidwhile an invoice is overdue:since(when it first failed),promotionsRefusedAt(14 days later, when the organization's new promotions are refused withplan-requiredandreason: "unpaid") andofflineAt(45 days later, when the project's live goes offline until it is paid), or null. -
8a5b7d3: Add
settings.amount({ label, default, decimals? })andsettings.chain({ label, default })to@domino-sdk/relay/authoring. An amount setting holds a non-negative base-unit integer string such as"1000000000000000000"; withdecimals, Console shows and edits it in whole tokens. A tracker filter bound may name an amount setting (gte: { setting: "minimum" }) and compares it exactly, so 18-decimal thresholds fit. A chain setting holds"solana", Solana mainnet's CAIP-2 ID or an EVM chain ID such as"eip155:8453", typed as thectx.wallet.balance()query'schain. Both are text settings (picker: "amount"or"chain"; text fields gain an optionaldecimals), and publication and Console edits refuse other values.@domino-sdk/relay/schemaexportsisAmount,isBalanceChain,isBoundSetting,textSettingSchemaand theAmountSetting,BoundSettingsandBalanceChaintypes. Thetoken-holderexample and snippet use both settings and need noWalletBalanceQuerycast; their minimum is now in base units. -
c656f61: A quest's
completioncan be a function of the policy builders:completion: ({ keyed }) => keyed(({ tracker, settings }) => tracker.record.data.messageId). Its key callbacks,keyed(...)'s key andprogress(...)'sdistinct, read aQuestKeyedContexttyped from the quest:trackeris non-null and typed for a tracker trigger, andsettingshas the quest's settings, so thetracker!cast is no longer needed. The builders it receives areonce,keyed,streakandprogress.completion: keyed(...)built inline keeps working and keeps itsKeyedContext. Key callbacks now also receive the quest's parsedsettingsat runtime.runQuestreportsrun.diagnostics(andcreateTestContextitsdiagnostics): acompletion-key-ignoredentry when the evaluator returns acompletionKeythe completion policy ignores. Onlykeyed(...)uses it;once(),streak(...),progress(...)and content quests key completions themselves. The decision is unchanged, and hosted attempts record nothing new. The Domino skill's authoring and tracker references describe the builder form and which policies usecompletionKey. -
4aaea1a: X tracker refresh policy. Tracker status gains
pauseReason(operator,x-budget-exhaustedortest-window-ended; null when not paused),resumesAt(when a tracker paused on its X budget resumes on its own) andeffectiveRefreshSeconds(the interval a scheduled tracker actually runs at:refreshSeconds, but no shorter than the project's plan allows, 6 hours below Enterprise). All three are optional, so older servers' answers still parse. Known X posts are re-read every 6 hours for 48 hours after they were posted, daily until day 7, then never. A deployment whose X search tracker asks for less than the plan's minimum warnstracker-refresh-below-plan-minimumand still publishes. A spent monthly X budget pauses X trackers withx-budget-exhausted(live only while plan limits are enforced, test always) until the budget renews, and resuming one meanwhile answers 409x-budget-exhausted; a test X tracker pauses withtest-window-endedafter 14 days until resumed.domino trackers create|update --refresh-secondshelp names the plan's minimum.
Patch Changes
- 5a8a0a0: CLI paper cuts.
domino stage --commitaccepts a short or uppercase SHA: a short one resolves to the full commit in the checkout, failing with Git's reason when it is unknown or ambiguous; a--request-idresume still takes the full SHA the build request printed.domino apistrips one/management/v1the path already starts with, sodomino api POST /management/v1/staging/linkreaches/staging/link(a one-line note on stderr says so outside--json). A reader that closes stdout early, as indomino --help | head, ends the CLI with status 0 instead of an unhandled EPIPE.domino test-members act --wait [--timeout SECONDS]reads the attempt until it is no longer queued, evaluating or deferred and prints it with its status, reason and code; without--waitit prints the queued attempt as before. The deprecation notes of baredomino status,questsandreleasesname the typed read for each use, the help says that--jsonstdout carries exactly one JSON value while progress events go to stderr as NDJSON, anddomino build --helprecommends--out FILE --jsonfor a summary. Both packages now ship theirCHANGELOG.md.
1.54.0
Minor Changes
-
ee9e5f1: Add
management.analytics.read({ range, ledger })anddomino analytics get [--range 7d|30d|90d|all] [--ledger KEY], which read a project's activity in one environment fromGET /management/v1/analyticswithreadpermission. A range ends now and is bucketed by project-calendar day (7d,30d) or Monday week (90d,all, which starts on the day of the earliest dated record: the first membership, or an older imported completion). Each metric (sign-ups, active members, attempts, completions, rejections, attempts sent to staff review, one point ledger's points awarded, spent and net and members earning, fulfilled offer redemptions, accepted and qualified referrals) has a total, a value per bucket and the previous range's total. Referrals also reportacceptedQualified, how many of the range's accepted referrals have qualified since, for a share that never exceeds 100%. Each quest also has its decisions, rejection rate, top 5 rejection reasons and rejections by source. Offers and referrals appear only when the project has them, and a test environment section only in test.domino analytics getprints a readable summary, and the read with--json;analyticsReadSchemaandanalyticsQuerySchemaare exported from@domino-sdk/relay/server.managementResponsesfrom@domino-sdk/relay/servernow builds each reader on first use, asmanagementResponseSchemasdoes, so importing the entry retains about 3.5 MiB less heap. Each reader is built once and stays writable. -
63f7a14: Custom domains. An organization admin can attach a domain they control, such as
quests.example.com, to a project's live hosted app. Domino shows a CNAME tocname.domino.pageand a_domino.TXT record that proves the domain, issues its certificate, and serves the live app there once both are in place. While the domain is active, page loads on<project-slug>.domino.pageredirect to it.domino domains add|list|get|recheck|removeand Console Settings → Domains attach, check and remove the domain, and show each DNS record against what public DNS returned and Cloudflare's statuses word for word.management.domains.list(),.get(id),.add(hostname),.recheck(id)and.remove(id).- Root domains, wildcards, IP addresses and Domino's own domains are refused with
invalid-hostname. A project has one custom domain. - The
hosted-custom-domainscapability is now supported.
-
50246d7:
getHostEnvironment()from@domino-sdk/relay/browsertells a hosted app where it is running: it resolves to{ environment: "test" | "live", kind: "staging" | "preview" | "live" }from the host's/__domino/environment, which Domino answers itself on staging (behind staging access), previews and the live app. It asks once per page load, and resolves tonullwhere the host does not answer, such as a local dev server, a host outside Domino, or server rendering. Use it instead of hostname patterns, which are not a supported signal. -
f5bd728: Test members can accept a referral invitation:
management.testMembers.acceptReferral(member, { code })or{ inviter }(another test member, whose invitation code is used), anddomino test-members accept-referral MEMBER --code CODEor--inviter MEMBER. The acceptance runs as a signed-in member's would, with the same refusals, qualification and rewards, in the test environment only, and the audit names the operator.
1.53.1
No changes in this release.
1.53.0
Not published to npm. Its changes first reached npm in 1.53.1.
Minor Changes
- 1b789ce: Test members can hold real X and Discord accounts.
management.testMembers.linkAccount(member, { actionId, provider, id }), or{ actionId, provider: "x", username }resolved through X, links a public account to a test member without OAuth, andunlinkAccount(member, { actionId, provider, id })unlinks and frees it (domino test-members link-accountandunlink-account). Quest code'sx.profile(),follows(),reposted()and Discord role reads then answer for the real account, tracker records it authors are attributed to the member, andtestMembers.recordposts as it instead of refusing withconflict. An account another member holds is refused withaccount-linked-elsewhere; when its owner signs in with it or connects it, it moves to them. Reset unlinks it. Relay never writes to a test-linked account: a Discord role reward for one creates no delivery and is never sent, and the attempt'shistoryentry carriesrewardsNotSent: [{ reason }]. Test member results include an optionalaccountslist; managed member identities andmembers.oauthAccountsmark these accounts with an optionaltestLinkedflag.
1.52.1
No changes in this release.
1.52.0
Minor Changes
- 9fadb80: Evaluator rejections take an optional code.
decision.reject({ reason, code })from@domino-sdk/relay/authoringnames the cause with a stable key, such aswrong-answers, that stays the same when the reason's wording changes. Relay stores it as the attempt'scode, returned bymanagement.attempts.getandmanagement.attempts.list,domino attempts list|getand Console's attempt detail beside the reason; responses to members carrycode: nullfor an evaluator-rejected attempt. A code uses lowercase letters, digits and hyphens, at most 64 characters, and cannot be one of the codes Relay writes itself, now exported asreservedAttemptCodesfrom@domino-sdk/relay/schema; an invalid one fails the evaluation, andrunQuest, with an error naming the rule. A content quest'sperformance.evaluateaccepts the samecode. Rejections without a code are unchanged.
1.51.0
Minor Changes
- 4dddeb0:
@domino-sdk/relay/serverexportscustomDomainSchema,customDomainListSchema,customDomainRecordSchema,customDomainStateSchemaandcustomDomainInputSchema, the shapes of the management API's custom domain routes (/management/v1/domains).
1.50.0
Minor Changes
-
2ecde12: An operator can pass a quest for a test member without running its check, or accept a test member's rejected attempt anyway, to reach the quests behind a check that can't be satisfied in test.
- New
management.testMembers.pass(member, { actionId, quest, completionKey? })admits an attempt on a manual quest as a submission would, with no input, then accepts it as the operator. Prerequisites, availability, enrollment and the retry cooldown still refuse it, and the completion policy, budgets and rewards apply as for an accepted check: a progress quest counts one step, and a keyed quest withoutcompletionKeygets a fresh key. From the CLI,domino test-members pass MEMBER QUEST [--completion-key KEY]. - New
management.testMembers.acceptAnyway(member, { actionId, attempt })accepts a rejected attempt as the operator, such as a tracker-triggered attempt its check rejected. The attempt keeps its rejection in its history, and when it is the member's latest attempt on the quest, its retry cooldown ends. Any other status is refused with 409attempt-not-rejected. From the CLI,domino test-members accept-anyway MEMBER ATTEMPT. - Both answer the decided attempt and record a
test-member-decisionevent with the operator as actor, listed in thetest-membersaudit category.testMembers.historyattempts an operator decided carryoperatorDecision: { kind, actor, at }.
Both need
publishpermission in the test environment, like the other test-member methods; live refuses them. - New
1.49.1
No changes in this release.
1.49.0
Minor Changes
-
c262df2:
management.attempts.listtakesquestto list one quest's attempts, by its ID, alone or withqandstatus(managedAttemptQuerySchema.quest).domino attempts list --quest <id>does the same from the CLI. -
41a3fe9: Hosted apps go live. Approving a promotion that carries a hosted app build deploys that build as the project's public live app at
https://<project-slug>.domino.page, with live's own management key (DOMINO_ENVIRONMENTislive), app secrets, KV and SQL stores, and schedule signing key. The previous live build serves until the new one switches in; a failed deployment keeps it, and promoting the build again retries. Before the first such promotion the address answers 404 "Nothing is live here yet".domino secrets,domino storageanddomino schedulestake--environment liveto manage the live app's secrets, stores and jobs. Without the flag they use test, as before; they ignoreRELAY_ENVIRONMENTand the environment inrelay.json.- A CLI sign-in session can set and delete app secrets in live. Other live writes still need Console or an API key.
management.liveApp.get()returns the live app's address and state (none,deploying,liveorfailed, with the failure). Console shows it as the Live app card in Deployments.- The
hosted-live-appcapability is now supported.
1.48.0
Not published to npm. Its changes first reached npm in 1.49.0.
Minor Changes
- 3213a87: Add group membership quest rewards.
groupMembership(group)from@domino-sdk/relay/authoringtakes adefineGrouphandle and adds the member to that group when the completion's rewards are delivered, recording the completion as the reason in the audit history and checking prerequisite-only automatic quests. Publishing refuses a reward for a group that does not exist or is archived. Revoking the completion removes the membership unless an operator's add or another unrevoked completion also backs it;CompletionRevocation.groupsreports each group withstillMember. Archiving a group that a quest rewards fails withgroup-in-use, naming the quests. SDK 1.x keeps the existingkind: "tier"reward envelope with an optionalreward.groupkey (groupRewardandgroupRewardSchemain@domino-sdk/relay/schema), and quest views add an optionalreward.groupwith the group's key and title.
1.47.0
Not published to npm. Its changes first reached npm in 1.49.0.
Minor Changes
-
7db52a2: Reward X posts only for approved creators. X does not allow apps that reward users for posting, so a deployment that publishes a quest rewarding X posts without an
inGroupprerequisite now returns the warningx-post-rewards-ungatedinwarnings, naming the quests by key. It applies to an X tracker trigger that pays the post's author and to a content quest on an X tracker, when the quest declares rewards or an engagement rule credits it;beneficiary: "inReplyTo"quests are not affected. The deployment still publishes,domino deployprints the warning, and Console shows it on the deployment with a link to Groups. Addrequires: [inGroup(group)]with a group of approved creators you manage in Console Groups, withdomino groups, or withmanagement.groups.The posting-streak and original-post SDK examples, the tracker guides and the CLI skill references now gate X post rewards on an
approved-creatorsgroup.
1.46.0
Minor Changes
-
fc0fc19: One deployment holds up to 1,000 quests and 200 quest types (was 50 each).
projectDeploymentSchema.releases,batchReleaseSchema.releases,questDraftChangesSchemaquests and collections, andcatalogDeploymentSchemacollections accept up to 1,000; the limits are exported asquestScaleLimits.domino build,deployanddevaccept up to 1,000 quests and 200 quest types in one manifest. Other resource kinds keep their limits.SDK clients older than this release check drafts against 50 quests and 50 collections, so they cannot read a draft that holds more. Update the SDK before building drafts larger than that.
1.45.0
Minor Changes
- 8bb0a7b: Quest and quest type bundles import the SDK instead of carrying it. The SDK package ships its runtime (
dist/runtime), andbuildQuest(entry, { runtime: true })from@domino-sdk/relay/buildleaves@domino-sdk/relay/authoring,@domino-sdk/relay/schemaand, at the matching version,zodout of the bundle, returning theruntimethe code imports.domino build,deploy,devand hosted builds use it: releases and types record aruntimereference and the deployment carries each runtime once inruntimes. A quest bundle shrinks from about 170 KB to a few kilobytes. Apps on an SDK without a runtime keep building self-contained bundles. A Relay that predates runtimes refuses such a deployment, and the CLI says to update it. See Shared SDK runtime.
1.44.0
Minor Changes
- f114841:
management.audit.listacceptscategory: "test-members", which lists operators acting as test members in test: creating, submitting a quest as, advancing the clock of, giving and removing wallets for, sending synthetic records as, signing a staging app in and out as, and resetting a test member. Listing every category now includes these records too. The newauditListQuerySchemaexport validates this query andAuditQueryis its input type;auditQuerySchemakeeps the three earlier categories and is deprecated in its favor. Console's Audit history shows these records under a new Test members filter.
Patch Changes
- f114841:
management.testMembers.resetanddomino test-members resetnow unlink the accounts a test member connected in your app while a staging site acted as it: X, Discord, Telegram, Google and Apple accounts and wallets. Before, such an account made reset refuse withtest-member-reset-blocked. Only the member's link is removed; the account itself is untouched and anyone can link it afterwards. Reset still refuses a member with an account that signs in, an app user ID, or an X account a member import attached. See Reset and create another member.
1.43.0
Minor Changes
-
64e8df3: Test members can post on X, send Discord messages and add Discord reactions, so quests on managed X and Discord trackers can be tried in test without real accounts.
- New
management.testMembers.record(member, { actionId, tracker, data?, as? })writes one synthetic record into a managed X or Discord tracker.datauses the stored record's field paths (contentandlikeCountfor X,channelIdandtextfor messages,emojifor reactions), so it exercises quests'wherefilters; Relay sets IDs and real time.as: "messageAuthor"makes the member the reacted-to message's author. The answer is{ tracker, record, account, stored, admissions }. From the CLI,domino test-members record MEMBER --tracker TRACKER [--data JSON] [--as author|messageAuthor]. - The first record for X or Discord links the test member a synthetic account of that kind, which attribution, member
identitiesandx.connection()read like a real link. Reset removes it with the member's synthetic records. - Tracker record reads mark these records
synthetic: true. Collected and ingested records never carry it. - Console's Try tab sends posts, messages and reactions for X and Discord quests, starting from values that match the quest's filter, and the tracker's record list marks synthetic records.
It needs
publishpermission in the test environment, like the other test-member methods; live refuses it. - New
1.42.0
Minor Changes
- b7156c7: Releases and quest types can reference a shared SDK runtime instead of bundling the SDK.
releaseSchema,publishedReleaseSchema, quest type versions and catalog deployment types take an optionalruntimereference ({ digest, sdkVersion }), and project deployments takeruntimes, each runtime's modules sent once. The new@domino-sdk/relay/schemaexports aresdkRuntimeSchema,sdkRuntimeReferenceSchema,sdkRuntimeModulesSchema,sdkRuntimeModulePathSchema,sdkRuntimeLimitsandsdkRuntimeDigest. Existing self-contained bundles work unchanged.
1.41.0
Not published to npm. Its changes first reached npm in 1.42.0.
Minor Changes
- c8e46ee:
RelayProviderreads the session and every query again whenwindowreceives adomino:test-state-changedevent. Domino's staging test toolbar fires it after it changes test data under the page, such as moving a test member to the next day or sending a tracker record, so a staging app shows the new state without a reload. Apps that read Domino withoutRelayProvidercan listen for the same event. See trying quests on your staging site.
1.40.0
Not published to npm. Its changes first reached npm in 1.42.0.
Minor Changes
-
68bf9eb: Promote test to live as one reviewed request (ADR 0017). A promotion makes live match test for every definition, code-defined and Console-authored, matched by authoring key, and carries the hosted app's active staged build. It lists environment bindings live lacks (integrations, webhooks, participant sign-in, app secrets) as warnings and never copies them.
management.promotions.prepare()(test),history(),get(id),approve(id)andreject(id, { reason })(live, Console session), androllback(id)(live):POST /promotions,GET /promotions/history,GET /promotions/:promotion, andPOST /promotions/:promotion/approve,/rejectand/rollback.domino promote --jsonprepares one from test;domino promotions listanddomino promotions getread them.- New error codes:
promotion-empty(live already matches),promotion-changed(test or live changed since the promotion was prepared; approval changed nothing) andpromotion-decided.
management.deployments.promote(id)anddomino deploy --promotekeep their 1.x request and response shapes. Once the test deployment is published they now prepare a promotion of all of test, answered as a pending deployment record with no resources whose first warning, codeenvironment-promotion, names the promotion. Approve it as a promotion:deployments.getanddeployments.publishdo not accept its ID.Live now refuses direct code deployments.
domino deploy --environment live, with or without--preview, andmanagement.deployments.previewwith a client for live fail with 422promotion-requiredbefore the bundle is checked, whatever the credential; the CLI reports it asENVIRONMENT_UNSUPPORTED(exit 1) and no longer submits anything. Automation that deployed to live should deploy to test, then rundomino promote(ormanagement.promotions.prepare()) and have a person approve the promotion in Console. Live deployments submitted before this change can still be submitted, approved, rejected and rolled back. Request and response shapes are unchanged, so like the other live refusals this ships in a minor release.
1.39.1
Patch Changes
- 08f24a3: An operator who signed in to a project's staging app through Console and holds
publishcan now sign that app in as a test member, through the staging host's/__domino/toolbar/*endpoints. The test member gets an ordinary 8-hour participant session. Issuing and ending it is audited as the operator, and that audit survives reset. Reset now ends a test member's participant sessions instead of refusing. Live, members that aren't test members, password staging sessions, API keys, hosted keys and CLI sessions can't start one. A password staging session gets the new refusal codeoperator-required, which the CLI skill's error reference now lists.
1.39.0
Minor Changes
- a9b11a8: Participants read the point entries behind their score on a leaderboard.
relay.me.leaderboardHistory({ board, cursor? })(GET /v1/me/leaderboards/:board/history) and the React hookuseLeaderboardHistory(board)page through the signed-in member's own entries that the board counts: its ledger filtered by the board's window, quests and calculation, so every page together sums to the member's score. Entries have the same shape asrelay.me.pointHistory(), newest recorded first, and the page'speriodis the window the first page counted. A member the board excludes, or one outside its group, gets no entries.
1.38.1
Patch Changes
- 9b02ade:
questConfigurationfrom@domino-sdk/relay/serveraccepts any release-shaped value withconfiguration,fieldsandvalues, so callers no longer need the release's code to read its configuration.
1.38.0
Minor Changes
- 167a840: A quest can make a member wait before trying again after a rejection. Set
retryCooldownMs(1,000 to 604,800,000) on a quest members submit, or Retry cooldown on a quest made in Console, where a new quiz starts at five minutes. After the evaluator or a reviewer rejects the member's latest attempt, a new attempt is refused with 409retry-cooldownuntilretryAt(alsoRelayError.retryAt). Quest progress andQuestViewcarryretryAtwhile the cooldown runs;questStatusreportsretry-cooldown,canSubmitis false until then,isCoolingDown(view)is new, anduseQuestrenders again when it ends. Catalog quest instances take an optionalretryCooldownMs(nullfor none). A quest without the field keeps today's behavior: no cooldown.
1.37.1
No changes in this release.
1.37.0
Not published to npm. Its changes first reached npm in 1.37.1.
Minor Changes
- 701a0d8: Leaderboards can list every member.
defineLeaderboard({ includeUnscored: true })adds the board's eligible members who have no qualifying points to its standings,me,nearbyandplacementreads, with a score of 0. They share one rank after every member who scored and are ordered by member ID; the board's group and exclusion group still apply. Settlements leave them out, so they win no prizes. Boards without the option rank as before. - e1b3c70: A points reward can pay up to 2,147,483,647 points, the largest 32-bit signed integer, instead of 10,000. The new limit applies wherever
points()andpointsRewardSchemaare used: quest rewards, evaluator decisions, leaderboard prizes and referral rewards. Offer prices (offerPriceSchema) take the same maximum and still start at 1. Code that declares a larger amount needs this SDK version, and apps that read one need SDK 1.10.0 or later, whose response readers ignore these maximums. - ff024c7: Scheduled jobs can report what a run did. A job route answers with
scheduleResult({ outcome, summary?, metrics?, diagnostics? })from@domino-sdk/relay/server, and Relay records it with the run asresult. Anoutcomeofpartialorfailedrecords the run as failed even when the HTTP status is 2xx. Relay reads anyapplication/jsonanswer up to 16,384 bytes. An invalid or larger one leaves the status alone and sets the run'serrortoResult ignored: …. Each schedule keeps its latest 20 runs:management.schedules.runs(name)(GET /schedules/:name/runs,domino schedules runs NAME, Console Run history) returns them newest first, and needsreadpermission. - ff024c7: Read a hosted app's SQL value in full where a rows page cuts it at 2,000 characters.
management.storage.cell(name, table, { row, column, cursor? })(GET /storage/sql/:name/cell?table=&row=&column=,domino storage cell DB TABLE ROW COLUMN [--all]) returns up to 100,000 characters per page with the value'stypeandlength, and anextCursorfor the rest;--allfollows it.rowis the row's position in storage order, the rows page'soffsetplus its index on the page. A BLOB answers with its size and no text. It needspublishpermission, like the other storage reads. Console's row view has Show full value.
1.36.0
Minor Changes
- 5da9393: Participants read their own email address.
relay.me.get()(GET /v1/me) anduseMe()now returnemails, the verified addresses that sign the member in (each email-code address, then the verified email of each Google or Apple account enabled for sign-in), andemail, the first of them for "signed in as". A member who signs in only with a wallet, X, Discord, Telegram or an app login has none:emailis null andemailsis empty. Participant routes never return another member's email. The classic starter shows the address in its account menu and Settings page.
1.35.0
Not published to npm. Its changes first reached npm in 1.36.0.
Minor Changes
-
f7ba8d6: Create and change X search trackers without a deployment.
management.trackers.create({ key, title, query, window, refreshSeconds?, maxPages? })(POST /trackers,domino trackers create) creates one as data in test, with the same validation asdefineTracker; its first collection searches fromwindow.start.management.trackers.update(tracker, changes)(POST /trackers/:tracker/update,domino trackers update) changes any field but the key, and a new query or window never searches the past. Both needpublishpermission, and live refuses them withpromotion-required: a data tracker reaches live through a promotion from test, which is not available yet. Console Trackers creates and edits them too.A key belongs to code or to data. Tracker status reports it as
origin(codeordata). A deployment that declares a data tracker's key is refused with 409tracker-owned-by-data, and creating or updating a key a deployment publishes with 409tracker-owned-by-code.
1.34.0
Minor Changes
-
81d0037: Definitions change in test and reach live only through a promotion (ADR 0017). In live, every definition write fails with 422
promotion-required, whatever the credential, Console sessions included. These 1.x management routes refuse live:POST /releases,/releases/batch,/releases/settings,/releases/lifecycle, and/quests/:quest/archiveand/unarchivePOST /catalog/deploy,/catalog/quests,/catalog/draftsand/catalog/publishPOST /engagement/publishand/engagement/changePOST /point-ledgers,/point-ledgers/update,/point-ledgers/:ledger/update,/archiveand/unarchivePOST /calendarand/profile-settingsPOST /badges,/badges/:badge/update,/archiveand/unarchivePOST /pickup-inventoryand/leaderboards/:board/exclusion-groupPOST /stock-items,/stock-items/:item/rename,/archiveand/unarchivePOST /resources/lifecycle, and/archiveand/unarchiveunder/trackers,/referral-programs,/leaderboards,/engagement/rules,/collections,/quest-typesand/images
API keys used to get 403
console-approval-requiredon most of these. Pickup policy, exclusion-group bindings and stock item creation, renames and archive state accepted API keys in live. Operations keep working in live under the caller's permissions: pauses, points, groups, moderation, reviews, entitlements, redemptions and stock adjustments. Code deployments still reach live through deployment review in Console.This ships as a minor release as an accepted exception to the compatibility policy: no request or response shape changes, the refusal is one new error code, and no project had written a definition to live in production. The CLI reports the refusal as
ENVIRONMENT_UNSUPPORTEDwithapiCode: "promotion-required".
1.33.0
Minor Changes
- 3706733: Add member imports: bring members, opening point balances and quest progress from another platform into a project and environment.
domino import --file members.jsonl --source NAME --dry-runuploads a JSONL file, checks every row and prints what applying would create, link, set and refuse; without--dry-runit applies the import in test.domino imports list|get|rows|apply|cancelandmanagement.memberImports.create|addRows|check|apply|cancel|get|rows|listreach the same/management/v1/member-importsroutes. A live import is applied only from Console. Imported opening balances areadjustentries that name their import (PointsEntry.import, andimportedin a member's own point history); they emit nopoints.awardedand count on balance leaderboards only. Imported progress pays no rewards. Accounts an import vouches for carryimported: trueonManagedWallet,ManagedOAuthAccountandManagedMemberIdentity.
1.32.0
Minor Changes
-
d6ea67b: Service keys give a self-hosted app's backend a management credential that no person owns. An organization admin creates one in Console under Settings → API keys for one project and environment, with chosen permissions. It never expires unless an expiry is chosen, and it keeps every live fence a personal key has.
management.serviceKeyslists, creates and revokes them from an admin's Console session. Rotation overlaps:addSecretissues a second secret while the first keeps working, andrevokeSecretretires the old one. Each secret reportslastUsedAt.Identity integration keys follow the same policy. New keys never expire unless
expiresInDaysis given, and existing keys keep their expiry until an admin removes it withidentityIntegrations.removeSecretExpiry.identityIntegrations.addSecretandrevokeSecretrotate a key without signing members out. Integrations now list theirsecrets.expiresAtis deprecated: it reports the latest secret expiry, or 8640000000000000 when a secret never expires.identityIntegrations.rotateis deprecated too. It now replaces every secret with one that never expires, instead of extending the key by 90 days.
1.31.0
Minor Changes
-
07802d2: Reward deliveries, redemptions and deployments page by
cursorandnextCursor, so long histories no longer stop silently.management.rewardDeliveries({ member, cursor, limit })andrelay.me.rewardDeliveries({ cursor, limit })answernextCursorbesidedeliveries. A page holds up to 500 deliveries, 500 by default, so calls without a cursor read what they did before.management.rewardDeliveries(member)still works.- New
management.redemptions.history({ member, cursor, limit })answers{ items, nextCursor }, 25 a page by default and at most 100, and replacesmanagement.offers.redemptions, which answers every redemption at once. From the CLI,domino redemptions history [--member] [--cursor] [--limit]replacesdomino redemptions list. - New
management.deployments.history({ cursor, limit })answers{ items, published, nextCursor }, 25 a page by default and at most 100.publishedholds the two newest published deployments by revision on every page: the live one and the onerollbackreturns to. It replacesmanagement.deployments.list, which stops at the 100 newest.
The deprecated forms keep working through 1.x and are removed in 2.0.0.
Patch Changes
- caf698a: To prepare project slugs for hosts on
domino.page, the server now refuses a slug change to a name Domino reserves (409Project slug is reserved) or to a slug shorter than 3 characters (400), and a new project's derived slug gets a short suffix instead of taking either. Reserved names match exactly:consoleis reserved,console-toolsis not.projectSchemastill parses every existing slug; itsslugfield documents the rule, and the agent references anddomino capabilitiesdescribe it.
1.30.1
No changes in this release.
1.30.0
Minor Changes
- cd6d78c: Add
management.x.lookup({ username })anddomino x lookup HANDLE, which resolve an X handle (with or without@) to the account that holds it,{ id, username, name }(read through a cache up to a day old), fromGET /management/v1/x/users/lookup. Trackers,--authorfilters, customxidentities andctx.x.follows({ userId })take the stableid, which no longer has to be copied from a post in Preview X search. The lookup needsreadpermission, fails with 404not-foundwhen no account holds the handle, and allows 30 lookups a minute per user and 300 an hour per organization. It resolves the handle only: it does not show who owns the account.
1.29.0
Minor Changes
- 272a255: Add
management.members.progress(member)anddomino members progress MEMBER, which read a member's progress on each active quest, 25 most recent attempts and point balances fromGET /management/v1/members/:member/progress. They leave out the quest releases and evaluator code thatmembers.getreturns beside them. In a project with many quests, member detail runs to megabytes; every list in this read is bounded by the project's configuration, so it suits scheduled jobs and other reads under a deadline.members.getis unchanged.
1.28.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
932e3c1: Add kind-keyed operator identity commands.
management.members.identityStates.list(member)reads every identity a member proved (wallets, X, Discord and Telegram accounts, andapp:<name>identities) with its link, revision, sign-in and allowed actions.identityStates.unlinkandrelinktake anidentityselector, anexpectedRevisionand a reason, andidentityStates.setSignInturns a wallet's sign-in on or off on its own. Each kind keeps its rules: wallet and account unlinks keep sign-in, app identity unlinks end that identity's sessions, and a Telegram account can now be relinked. The CLI addsdomino members identities,unlink-identity,relink-identityandidentity-sign-in.Add
management.discordRoles.preserve(delivery, { reason }), the Discord role reward kind's preservation operation. A delivery of a reward kind without that operation is refused with 409.Deprecated, still supported through 1.x:
members.oauthAccounts,unlinkOAuthAccount,telegramAccount,unlinkTelegramAccount,unlinkWallet,setWalletSignIn,unlinkIdentity,relinkIdentityandidentityLinks(usemembers.identityStates),management.preserveRewardRole(usemanagement.discordRoles.preserve), and the CLI'smembers identity-links,members relinkandmembers oauth-accounts.
1.27.1
Not published to npm. Its changes first reached npm in 1.29.0.
No changes in this release.
1.27.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
072493d: Leaderboard exclusions are a group (DOM-205):
- A board leaves out the members of its exclusion group. Each board gets its own group, keyed
domino--exclusions--<board ID>, on its first exclusion. Existing exclusions and their history move into these groups, so standings and settlements leave out the same members at every cutoff. management.leaderboards.exclusionGroup(board)returns{ leaderboard, group, generated, boundAt }, andmanagement.leaderboards.bindExclusionGroup(board, { group, reason })binds the board to another unarchived group, such as one several boards share, or back to its own. Settlements read the group bound at their cutoff.management.leaderboards.excludeandexclusionskeep their inputs, responses, permissions and audit events. They now change and read the board's exclusion group, whichmanagement.groupscan change too.- Archiving a group that a leaderboard uses as its exclusion group fails with 409
group-in-use, naming the boards inleaderboards. Group keys starting withdomino--exclusions--are reserved: creating one fails with 409already-exists. - CLI:
domino leaderboards exclusion-group BOARDanddomino leaderboards bind-exclusion-group BOARD GROUP --reason ….
- A board leaves out the members of its exclusion group. Each board gets its own group, keyed
1.26.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
91d7ad0: Relay keeps quest submissions (DOM-268). See ADR 0014.
- Submission handles (
me.submission) and quest controllers (quests.open) save each submission in Relay instead of the browser. A lost answer, a reload, another tab or another device signed in as the same member recovers it from Relay, and submitting again returns the same attempt. The public API is unchanged. Submissions no longer need IndexedDB or Web Locks, socreateRelayClientwithout asubmissionStoresubmits too.SubmissionStoreandbrowserSubmissionStoreare used only to finish a submission an older SDK version saved in the browser. - A member has at most one unfinished attempt per quest. Another claim, or a retry of a failed attempt, is refused with
attempt-pendinguntil the previous one is decided, including while it waits for human review. The same action sent twice still replays. - New participant routes:
GET/POST /v1/me/quests/:quest/submission,GET/POST /v1/me/submissions/:submission, andPOST /v1/me/submissions/:submission/submit,/discardand/evidence(a photo uploaded into the submission). A submit refused because the quest changed keeps the draft withreviewRequired, and the handle reportsneeds-review.@domino-sdk/relay/schemaexportsquestSubmissionSchema,questSubmissionUpdateSchema,questSubmissionResultSchemaandQuestSubmissionStatus. A draft expires 7 days after its last change. Refusal codesubmission-closedmeans the submission was already submitted, discarded or expired. - The management client has
quests.submissions(member)andquests.discardSubmission(member, submission). Console shows a member's open quest submissions and lets reviewers and staff discard one.
- Submission handles (
1.25.1
Not published to npm. Its changes first reached npm in 1.29.0.
No changes in this release.
1.25.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
55d08ae: Client SDK paper cuts (DOM-160):
createRelayClienttakesgetToken: async () => token | null, read before every attempt, as the management client does. A failinggetTokenrejects with codecredentialbefore anything is sent.tokenandgetTokentogether are refused.createRelayClientandcreateManagementClienttakefetch, which replaces the globalfetchfor every call, andheaders, added to every call. The client's own headers win over one of the same name; an invalid header is refused withinvalid-inputwhen the client is created.- Submission handles (
me.submission) and quest controllers (quests.open) take{ signal?, timeout? }onsubmit,resume,statusanddiscard. Each request the call sends (session read, upload, claim, retry, and a controller's read of the latest quest afterquest-updated) gets the signal and what is left of one deadline for the whole call. A cancelled or timed-out call keeps its saved progress forresume(). The quiz'ssubmit(answers, { release?, signal?, timeout? })makesreleaseoptional. auth.start({ provider, mode })andauth.emailVerify({ challenge, code })take an options object, which refuses unknown fields. The positional forms still work and are deprecated, to be removed in 2.0.0.paginate(read)from@domino-sdk/relayiterates every item of a paged read withfor await, followingnextCursor.RelayErrorCodelistsOperationRetired(operation-retired) andCredential(credential).offers.redeem,profiles.badges,me.activity.list,me.upload,auth.telegram.miniApp.connectandauth.emailStartreject invalid input with aRelayErrorof codeinvalid-input, instead of throwing a rawZodError(offers.redeem) or throwing synchronously.- The browser bundle no longer includes every Zod locale:
createBrowserRelayClientdrops from about 412 KB to 198 KB minified (90 KB to 56 KB gzipped).
1.24.1
No changes in this release.
1.24.0
Minor Changes
-
f6e030a: Webhook endpoint creation and secret rotation can be recovered after a lost answer.
management.webhooks.createtakes an optionalactionId(newwebhookCreateInputSchema), andmanagement.webhooks.rotateSecret(id, { actionId })takes one too (newwebhookRotateInputSchema). The client generates an ID when you omit it. A retry with the sameactionIdreturns the same endpoint and signing secret instead of creating a second endpoint, or being refused during the 24-hour overlap. Once the secret is rotated again, the earlier action's retry is refused withsecret-superseded.domino webhooks createanddomino webhooks rotate-secretare now saved operations with--operation-id; resume one withdomino operations resume <id>.Relay now refuses webhook subscriptions to event types it never sends, such as
completion.create, withunknown-activity-type; the message lists the valid types. This narrows what the API accepts, but the CLI and SDK already report it as a refusal. An endpoint update can keep a type the endpoint subscribed to before. Event payloadtypestays an open string, so receivers must still accept or ignore types they do not know.
1.23.2
No changes in this release.
1.23.1
Patch Changes
- fd5f211: Members now belong to one project. A person who signs in to two projects of the same organization is a separate member in each, with their own identities, linked X, Discord and Telegram accounts, and wallets; unlinking, relinking and wallet sign-in control act within the project. Project transfer now moves a project with its participants instead of refusing it. No API shape changes:
MemberIdentityLink.scopekeeps the value"organization-environment"for OAuth links, which now names a link in the member's project and environment.
1.23.0
Minor Changes
-
88c7c00: Add Telegram chats, membership checks and reaction trackers. Operators connect Telegram groups and channels to a project through Domino's bot in Console (Integrations → Telegram → Add chat), or with
management.telegram.connect(),chats()andremove(chat). See Telegram.ctx.telegram.member({ chat })reads whether the member's linked Telegram account is in a connected chat:{ userId, username, status, isMember, isAdmin, untilDate }, or null. It needs nointegrationsentry.ctx.telegramis optional inEvaluationContext, so code that builds contexts keeps compiling.settings.telegramChat()adds a setting that Console fills from the connected chats. The testing kit takesfacts.telegram.member.telegram.reactions()is a tracker source of the reactions members add in connected groups. Each record carries Telegram accounts for the reactor (author) and the message's author (messageAuthor).dataisTelegramReactionData, which includesreactorStatusandreactorIsAdminfor a trigger'swhere. In SDK 1.x it is a custom source on the reserved namespacetelegram-reactions, which Relay alone writes.- Text settings can carry
picker, which Console uses to offer a list. - The
telegram-membershipsnippet is a join quest.
1.22.0
Minor Changes
- 81beec3: Personal API keys and
domino loginCLI sessions can now use test members, including giving them wallet addresses, not only Console sessions: the caller needspublishpermission in the test environment, and hosted app keys stay refused. Adddomino test-members create|list|history|act|advance|link-wallet|unlink-wallet|reset, which always address the test environment and save their writes as resumable operations, so a coding agent can simulate a campaign end to end.
1.21.0
Minor Changes
- 5db81c1: Test members can hold wallet addresses.
management.testMembers.linkWallet(member, { actionId, chain, address, network? })gives a test member a Solana, EVM or Cosmos address without a signature, andunlinkWallet(member, { actionId, chain, address })removes and frees one. Quest code reads these addresses throughctx.walletlike a member's linked wallets, so balance-gated quests can be tried in the test environment. When the wallet's owner signs in with it or connects it, the address moves to them. Test member results include an optionalwalletslist. The CLI's capability map and Domino skill reference describe the new methods.
1.20.0
Minor Changes
-
2e1eff4: Add Telegram sign-in. Telegram Login signs participants in on the web where Domino configures it, and Mini App sign-in signs them in inside a project's own Telegram Mini App once the project names its bot ID in Console (Settings → Sign-in). Both reach one Telegram account. See Telegram.
Telegram is not an
AuthProviderin SDK 1.x, so code that switches over providers keeps working:AuthProvider,relay.auth.accounts(),useAccounts()anduseSignInOptions().optionsnever include it. Telegram has its own additions beside them:relay.auth.providers()returnstelegram: { login, miniApp }.relay.auth.telegramhasstart(mode),account(),disconnect(), andminiApp.signIn(initData)andminiApp.connect(initData).telegramMiniAppInitData()reads the Mini App's launch data.useSignInOptions()andsignInOptions()return the Telegram Login option astelegram, besideoptions.useSignIn()addswithTelegram()andwithTelegramMiniApp(initData), anduseTelegramAccount()connects and disconnects the member's Telegram account.- In the management client,
participantSignIn()returnstelegram: { login, miniAppBotId }, andconfigureParticipantSignIn()takestelegram: { miniAppBotId }, which keeps the current bot when left out.members.telegramAccount(member)andmembers.unlinkTelegramAccount(member, { subject, revision, reason })read and unlink a member's Telegram account.
1.19.0
Minor Changes
- 37a57df: Deploying a quest that needs an unconnected integration, such as Discord, no longer fails. The deployment preview and result include
warnings(deploymentWarningSchema) naming the quests with codeintegration-required, anddomino deployprints them. Their attempts still fail withintegration-requireduntil the integration is connected.
1.18.1
Patch Changes
- 0a6ebf1: Allow management deployment responses to include additional fields in direct, promotion and rollback sources. This closes the recorded response compatibility gap without changing known fields or source kinds.
1.18.0
Minor Changes
- 7000d41: Add project badge definitions, quest badge rewards, audited manual awards and revokes, participant and profile reads, and React hooks. Author badges with
defineBadge, include them inrelay.json, and attach them withbadge(...). SDK 1.x retains the existing reward envelope with optional badge metadata. The CLI addsdomino badgescatalog and member commands.
1.17.0
Not published to npm. Its changes first reached npm in 1.18.0.
Minor Changes
- d8a5731: Add ordered quest presentation steps with HTTPS link actions, images and alt text, and account or wallet hints. Collection declarations now accept description, image and alt text. Participant reads and React hooks return these optional fields in the published collection order. Console can edit and reset quest presentation and edit collection details. Presentation changes do not change verification.
1.16.0
Minor Changes
-
b9d767f: Run background jobs on active hosted Worker builds in test. Declare
app.schedulesin relay.json with an interval or UTC cron, verify signed route calls withverifyScheduledRequest(request, env.DOMINO_SCHEDULE_KEY), and inspect or control jobs withmanagement.schedulesordomino schedules.Jobs have a five-minute minimum cadence, ten schedules per app and a 30-second timeout. Relay makes one attempt per tick; uncertain completion blocks future runs until an operator confirms recovery. See Run scheduled jobs for custom tracker ingestion and recovery.
1.15.1
Not published to npm. Its changes first reached npm in 1.16.0.
No changes in this release.
1.15.0
No changes in this release.
1.14.0
Minor Changes
- 0c394de: Replay captured test-environment attempts locally.
management.attempts.capture(attempt)(publishpermission) returns what one evaluation run of an attempt read: the release's settings and rewards, the attempt's input, frozen clock, seed and calendar, each captured capability answer, and what quest code decided.replayAttempt(quest, capture)from@domino-sdk/relay/testingevaluates current quest code against it and reports the decision beside the recorded one, whether it changed (kind, reason, rewards or completion key), and the first call the capture could not answer.domino quests replay <attempt>fetches, saves (--out) and replays a capture. Captures are served from the test environment only: live attempts are refused with 422test-environment-required, and an attempt that has not been evaluated with 409attempt-not-evaluated. - 580a641: Add release history to the management client.
management.releases.list({ quest?, limit?, cursor? })(GET /management/v1/releases/list) pages every release the project's quests have had, newest first, optionally for one quest by ID or key, andmanagement.releases.get(release)(GET /management/v1/releases/:release) reads one current or historical release. Both needreadandpublishpermission. They replace the deprecatedpage("releases")and itsdetail.
Patch Changes
- cb252b6: The CLI now sends its management requests through the management client's own transport, so
dominoandcreateManagementClientshare one implementation of deadlines, retries and failure classification. CLI exit codes, error codes and results stay the same; some error messages change wording, and network errors no longer carrydetails.cause. A management client write whose connection was refused, never resolved, could not reach the network or host, timed out while connecting, or failed TLS now fails withuncertain: false, because nothing was sent, and a 204 answer reads asnull. - 3ccb72a:
Personrecords (members.people(), and thepeopleof attempt, entitlement and page reads) now carrytestandnamefor Console test members in the test environment, the same fields member reads already had. Console names test members by that name and marks them as test members instead of showing their member ID.
1.13.0
Not published to npm. Its changes first reached npm in 1.14.0.
Minor Changes
-
c3601dd:
RelayProvidernow follows the signed-in member's activity feed instead of polling the whole quest experience every 1.5 seconds. Each event reads again only the hooks it changes: an accepted attempt reads the experience, outcomes, rewards, balances, leaderboards, profiles, referral progress and offers; a points award reads balances, leaderboards, offers and deliveries; and so on. The whole experience is read every 30 seconds for changes no event announces, and as the fallback when the feed is unavailable. An idle signed-in tab drops from about 124 requests a minute to about 14. Passpolling={{ activityMs, snapshotMs }}to change either interval.useEntitlementsanduseDeliveriesnow poll every 30 seconds instead of 5, since feed events refresh them.quests.observe,me.observeProgressandme.activity.observemake one request per poll: they send the session with each read, and read it again only when Relay refuses the session as changed. A shown tab resumes polling on schedule instead of reading at once.me.activity.listandme.activity.observeacceptstart: "latest"to follow only new events.New React query options:
enabledandnullkeys for dependent queries,staleMs,select,suspense, andinitialDatafor public reads (rendered on the server and while hydrating).useSetQueryData()changes a cached value at once, for optimistic updates.Mutation hooks share one shape:
mutate,mutateAsync,status,isPending,data,error,resetandsettle, built onuseRelayAction's retained keys.useRelayMutation(mutation, { invalidates })gives your own writes that shape, anduseRedeemOfferanduseSignOutnow have it.useRelayActionaddsstatus,isPending,dataandreset, and its failed result carrieserror.useSignInanduseSignInOptionsaddisPendingandreset;useAccounts,useWalletsanduseMyProfileaddisPendingandresetAction;useQuestaddsisPending.Deprecated, still working:
busyon every hook that now hasisPending;useRedeemOffer'sredeemandretry(usemutate, which replays a retained redemption);useSignOut'ssignOut(usemutateAsync, which rejects the same way, ormutate).
1.12.0
Not published to npm. Its changes first reached npm in 1.14.0.
Minor Changes
- 80f823a: Add
@domino-sdk/relay/testing, a kit for unit-testing quests locally.runQuest(quest, { input, tracker, settings, facts, now, seed })evaluates a quest the way Relay's hosted runtime does: its published settings, rewards and review switch, a frozen clock, seeded randomness, fake answers forctx.member,ctx.wallet,ctx.x,ctx.discord,ctx.trackers,ctx.imagesandctx.reviewchecked against Relay's capability contracts, and the reward bounds Relay enforces.trackerObservationandtrackerPagebuild typed tracker input,expectAccept,expectReject,expectReview,expectDeferandexpectProgressassert decisions, andcreateTestContextreturns the host contextquest.evaluatetakes.buildQuestnow refuses a quest bundle that imports the testing kit. Starters include quest unit tests intests/quests.test.ts.
1.11.2
Not published to npm. Its changes first reached npm in 1.14.0.
Patch Changes
- 7160db6: Include points received before a leaderboard cutoff even when their transaction commits just afterward. New point receipts use database time, and freeze waits for in-flight point writers. Existing frozen settlements remain unchanged.
1.11.1
Patch Changes
- 90e5141: Build tolerant response readers on first use so SDK imports and backend startup retain less heap. Preserve concrete Zod schema APIs, shared readers, and unsupported-variant response envelopes.
1.11.0
Minor Changes
- 76e7a45: Add operator quest withdrawal, inventory rename/archive/unarchive/stock correction, webhook deletion with retained history, settlement rejection, and kind-keyed wallet/OAuth relink. Existing 1.x methods and response shapes remain supported. Inventory archive refuses retained offer and pickup references with named blockers; relink applies the current quest's historical policy while preserving admitted attempts.
1.10.1
Patch Changes
- fa55eff: Share converted response schema nodes to avoid excessive startup heap allocation in SDK 1.10.0. Tolerant response parsing and the SDK API are unchanged.
1.10.0
Minor Changes
-
f2724f9: Read added response fields at every depth without relaxing request validation. Add explicit supported/unsupported response schemas and
client.response(...)for extensible results. Unsupported entities retain their raw data; entity lists retain positions and report an unsupported count. Existing 1.x method return types stay compatible.SDK and CLI calls identify their package, version and runtime with
X-Domino-Client. Tolerant SDK calls also identify their per-call response capability withX-Domino-Response: tolerant-v1. Older and legacy callers still require compatible server wire shapes. See the participant response reference for the opt-in API.
1.9.0
Minor Changes
-
4ae54f9: Add
relay.action(),createRelayAction(), anduseRelayAction()to retain a mutation key and its original callback across uncertain failures, with explicit replay-safe retries and acknowledgment of confirmed reconciliation and a synchronous guard against duplicate clicks.useRedeemOffernow reuses the original redemption key and exposesretry.All previously required participant and management mutation action IDs are optional; omitted IDs are generated once per call. Only reusing a key protects separate calls from double application. Evidence upload now generates an action ID; avatar upload accepts
actionIdalongside the existinguploadIdalias and still requires reconciliation after uncertainty.See retry after an uncertain answer for helper lifetime, retry behavior, and reload recovery. Existing explicit keys and upload aliases continue to work.
1.8.0
Not published to npm. Its changes first reached npm in 1.9.0.
Minor Changes
- f19da0d: Add canonical archive, unarchive, pause and resume methods and CLI commands for management resources. Keep older lifecycle and feature-specific publication forms working as deprecated 1.x APIs.
1.7.0
Minor Changes
- 5662b50: Expose protocol validation helpers through
@domino-sdk/relay/serverfor server adapters: identity integration inputs and exchange, participant routes, quest configuration and shape validation, sequence cursors, image extraction, tracker data validation, and referral reward checks. Existing exports and wire formats are unchanged.
1.6.0
Minor Changes
-
4651887: Add an optional
windowdetail tooutside-windowavailability, errors, and automatic outcomes so clients can distinguish tracker and quest windows without branching on reason text. -
547600e: Every refusal from the participant and management APIs now carries a machine-readable
codebeside its unchanged message, so clients can branch without matching text.RelayError.codeandManagementError.code(and the CLI'sapiCode) are set for every API answer: a specific code when the client can act on it, such ascatalog-changed,draft-changed,prerequisites-incomplete,permission-requiredornot-configured, or the status's own code otherwise (not-found,conflict,forbidden, …). Structured detail, such as themissingprerequisites, comes with the code on every route. The API error codes reference lists them.RelayErrorCodeaddsPrerequisitesIncomplete,QuestClosed,QuestPaused,ProfileLocked,InvitationNotFound,InvalidCode,CodeExpired,ChallengeExpiredandRateLimited.- New
managementErrorSchema(@domino-sdk/relay/server) parses the management API's error body, for rawapi.raw()answers.
Messages keep their text, and
codestays an open string, so code written against 1.2 keeps working.The participant proxy now codes its own routing, configuration and authentication refusals. During application identity exchange it also keeps Relay's safe upstream code and
Retry-Afterwhile continuing to replace non-account-conflict messages with its existing generic text. -
6c80795: Groups are their own resource. A group is a named set of members that
inGroupprerequisites, a leaderboard'sgroupand a referral program'sinviterGroupread by key; before, groups existed only as leaderboard group memberships. Existing memberships and their history move over unchanged.management.groupslists, gets, creates, renames, archives and unarchives groups, and lists, adds and removes members with a reason for the audit history. Lists take{ cursor, limit }and return{ items, nextCursor }. The schemas andManagedGroup,GroupPage,GroupMemberandGroupMemberPagetypes are exported from@domino-sdk/relay/schemaand@domino-sdk/relay/server.domino groups list|show|create|update|archive|unarchive|members|add|removedoes the same from the CLI.- Groups are archived, never deleted. An archived group's members still count, but cannot change until it is unarchived.
management.leaderboards.group(group, { member, included, reason })andleaderboardGroupSchemaare deprecated and removed in 2.0.0. They keep working, and still create a group the first time they add a member to it. The group name must now be a group key (letters, digits,_and-, at most 100 characters), as definitions already required; a longer name answers 400. Replace them withmanagement.groups.create({ key, title })once, thenmanagement.groups.addMember(group, { member, reason })andmanagement.groups.removeMember(group, member, { reason }).
defineGroupandinGroupare unchanged. See groups. -
6088922: Every paged management read now takes
cursorand answersnextCursor, as participant reads already did: pass a page'snextCursorback ascursoruntil it isnull.management.points.history(member, ledger, { cursor })andmanagement.referrals.participants({ cursor })take an options object; their answers addnextCursor. Passing the numericbefore(a page'snext) still works and is deprecated.management.webhooks.deliveries(id, { cursor, limit })anddomino webhooks deliveries <endpoint> --cursor <cursor>page by the answer'snextCursor.before,--beforeandnextare deprecated.management.storage.rows(name, table, { cursor })anddomino storage rows NAME TABLE --cursor Cpage bynextCursor;offsetand--offsetare deprecated.management.storage.keysanswersnextCursor; itscursorfield is deprecated.- New
management.trackers.triggerOutcomes(tracker, { ...filters, cursor })answers{ items, nextCursor }and replacesmanagement.trackers.outcomes, whosebeforeandnextstay as they were. - New
management.engagement.ruleVersions(rule, { cursor })answers{ items, nextCursor }and replacesmanagement.engagement.versions({ rule, before }). resourceOptionsSchema(@domino-sdk/relay/schema) addsnextCursor; itscursorfield is deprecated.
The deprecated forms keep working through 1.x and are removed in 2.0.0. Responses gain only fields, so code written against 1.2 keeps compiling.
-
6fa9cd5: New
management.members.list({ q, needsReview, limit, cursor })(GET /management/v1/members/list) anddomino members list [--q TEXT] [--needs-review] [--limit N] [--cursor C]list a project's members, most recently joined first, with their accounts and attempt, completion and reward counts. The answer is{ items, total, nextCursor }: passnextCursorback ascursoruntil it isnull.qfinds members by part of their ID, profile name, or an account's subject, name or email, as Console's member search does. Use it instead ofpage("members", { q }).memberListSchema,memberPageSchemaandMemberPage(@domino-sdk/relay/schema), which no route used, are deprecated in favour ofmanagedMemberQuerySchema,managedMemberPageSchemaandManagedMemberPage(@domino-sdk/relay/server), and are removed in 2.0.0. -
e3a050e: Management commands on an existing resource now name it in the path, and the client methods take it as their first argument:
management.deployments.publish(id),submit(id),reject(id, { reason }),promote(id, { actionId? })androllback(id, { actionId? })call/deployments/:deployment/<verb>. The{ id, ... }forms still work and are deprecated.domino deployuses the new routes.management.pointLedgers.update(ledger, { name, description })calls/point-ledgers/:ledger/updateand replaces the deprecatedpointLedgers.save({ id, name, description }).management.referrals.suspend(member, { suspended, reason }),qualify(program, member, { qualified, reason })andresolveReward(entitlement, { reason })replace the deprecatedreferrals.control({ kind, ... }). Each generates itsactionIdwhen omitted.
Duplicate and misleading names have clear replacements; the old names stay as deprecated aliases:
management.points.record(input)awards, spends, adjusts, reverses or sets a balance, replacingpoints.update.management.pointLedgers.list()andpointLedgers.create(input)replacepoints.ledgers()andpoints.createLedger(input).
The
{ id, ... }input schemas (publishDeploymentSchema,submitDeploymentSchema,rejectDeploymentSchema,promoteDeploymentSchema,rollbackDeploymentSchemaandreferralControlSchema) are deprecated beside them;deploymentRejectionSchema,deploymentActionSchema,referralSuspensionSchema,referralQualificationSchemaandreferralRewardResolutionSchemadescribe the new bodies.The routes that took these IDs in the body keep answering released clients. Deprecated forms are removed in 2.0.0.
-
b145e16: The management client reads a project's state as resources, which Console now composes its pages from:
management.metrics.read(): the project's counts and each quest's completions (completionsByQuest).management.quests.list(): each quest's current release.management.operations.list(): paused and resumed operations.management.inventory.list(): stock items with their counts and the offers that sell them.management.attempts.list({ q, status, limit, cursor })andmanagement.attempts.get(attempt);management.entitlements.list({ q, status, limit, cursor })andmanagement.entitlements.get(entitlement).management.audit.list({ category, q, limit, cursor })and, for organization administrators,management.audit.access({ q, limit, cursor }).
The paged attempt, entitlement and audit reads answer
{ items, total, nextCursor }; attempt pages also carry the releases and members (people) they name, and entitlement pages carry their members. PassnextCursorback ascursoruntil it isnull.management.page(),pageSchema,ManagementPage,querySchemaandListQuery, Console's former page-shaped reads, are deprecated: compose the reads above instead. The page routes keep answering, unchanged, through 1.x; they are removed in 2.0.0. -
b3d87d0:
points.awardedwebhook events now carrysource,{ kind, id }, naming what paid the award:questorleaderboardwith the quest or board ID,engagementwith the engagement rule's key, ormanualwithid: nullfor an operator's point award.kindis an open string that can gain values;PayoutSourceKindfrom@domino-sdk/relay/servernames the known ones. A completion revocation result (CompletionRevocation, frommanagement.completions.revoke) gains the same optionalsourceas completion records, so a revoked leaderboard prize can be told apart from a quest completion;questkeeps carrying the board ID for a prize. See the webhook reference. -
f322ced:
ctx.review.photo()is deprecated and will be removed in 2.0.0. It still works, and nothing about how it reviews photos changes. In new quest code, review an uploaded photo withctx.images.review({ images: [{ evidence: ctx.input.photo }], criteria })instead. It returns the samepass,failorunclearresult, and it can also review X images, reference images and up to four images at once. See check images.The agent references and capability map now list tracker-restricted ingestion keys (
ingestTrackersonmanagement.tokens.createandmanagement.tokens.scope) as supported. -
5ddcb62: Approving a leaderboard settlement now sends a
completion.createdwebhook event for each winner's completion. Before, only quest attempts sent it. Everycompletion.createdevent now carriessource({ kind, id }, wherekindisquestorleaderboard) andkey, the occurrence the completion consumed. A prize event hasattempt: null, the board ID inquestandsource.id, and the settlement ID inkey. If your receiver treats everycompletion.createdas a quest completion, branch onsource.kind. Completion records in management snapshots gain the same optionalsource, andCompletionSourceKindis exported from@domino-sdk/relay/serverand@domino-sdk/relay/schema. See the webhook reference.
Patch Changes
-
75fa01c: Prerequisite-only automatic quests now try an uncompleted occurrence again after a claim-time budget rejection when a later member event passes current eligibility and budget checks. Evaluator, staff and revoked-occurrence rejections stay final. Automatic outcome reads can include optional
linkedAttemptdetails; the legacyattemptID remains available in 1.x but is deprecated. Operators can preview and confirm budget recovery through the existing automatic catch-up API and Console flow. -
3831c73:
proxyParticipantandcreateParticipantBackend(@domino-sdk/relay/portal-proxy) now forward each participant route only with the method Relay registers for it, from the same route list Relay's API admits hosted apps by. Before, some routes (sign-in and session,/v1/me/progress,/v1/me/experience,/v1/me/attempts,/v1/me/evidence,/v1/me/collection-pass, the wallet routes and quest retries) were forwarded with either GET or POST, and Relay answered the wrong one with 405. The proxy now answers it with 404 "Route not found". No client method changes behavior.relay.evidence.downloadis deprecated and will be removed in 2.0.0. No participant gate admitsGET /v1/evidence/:id, so it has always failed with 404; members do not read evidence back, and reviewers download it through the management API. -
f493c48: Referral corrections and operator reversals now consume one shared remainder for each positive payout, so either order settles the payout once. Historical corrections use the referral account's settled amount as a conservative cap. New referral definitions refuse Discord role rewards with a clear error. Stored legacy programs remain readable and recoverable, and skip an unsupported role with an Audit history reason instead of failing qualification. See referral corrections.
-
6e9b120: Completion revocation now keeps any points it could not take back visible after the command returns. The result and audit event report the remainder for each point ledger. Console shows the remainder on the member and attempt, and operators can open the existing point adjustment to settle it explicitly. Relay never records member debt for the remainder.
-
0ec4d7d:
RelayClientandManagementClientare now named interfaces thatcreateRelayClient,createBrowserRelayClient,createManagementClientandhostedManagementClientreturn, so declaration files name them instead of repeating the whole client.ManagementClient.testMembersis no longer optional: every client these factories create has it. The clients and the error classes (RequestError,RelayError,QuestUpdatedError,ManagementError) are not for implementation or subclassing; minor releases add methods and fields to them. A@compattag in a type's doc comment now says how it may grow in a minor release (Upgrade to Relay SDK v1). -
f6b705a: Revoking a completion now reads the member's recorded tier grants instead of every completion event in the project, so it stays fast as a project grows.
409 completion-grant-history-missingremains only for older completions whose original record could not be read; contact Domino support to restore their history. See Revoke a completion. -
65a9765: Deprecated three schema members that Relay no longer uses. They stay in 1.x and are removed in 2.0.0:
faultAfterReviewonsubmitSchema(SubmitInput). Relay refuses a submission that sets it totrue, so stop sending it.- The
fixture-*values ofstoredReviewModeSchema. They name the retired simulated review providers, and only releases and quest types published before the retirement carry them. Publication already accepts onlyworkers-ai. reservationonRedemption. A redemption is fulfilled in the request that makes it, so the ID names no separate hold. Useidto refer to a redemption.
Offer redemptions no longer briefly hold the price before spending it, and their response is unchanged. The photo, quiz and staff-confirmation snippet guides no longer suggest
fixture-*providers, which Relay refuses at publication.
1.5.2
No changes in this release.
1.5.1
No changes in this release.
1.5.0
Minor Changes
-
625dcbc: Expose project creator IDs and resolved operator details in project access and audit responses. Validate enriched profile IDs against the recorded creator or actor while accepting older unenriched responses. Add
updateProjectfor Console organization admins to rename projects or change their slug with name confirmation. Hosted build links follow the current slug without a rebuild, including older builds that used repository-based addresses. Links switch only after the new hostname is confirmed.Slug changes preserve app secrets and hosted stores. New staging addresses are registered before the change commits; old and prepared slugs remain reserved until project purge, so interrupted changes can be retried safely. Reserved slugs cannot be claimed by older servers during a rolling upgrade.
1.4.0
Minor Changes
-
da46e7b: Add
management.testMembers.advance(member, { actionId, days }, call?)andTestMember.offsetDaysfor Console sessions in test. Move one member forward 1–30 project-calendar days per write, up to 90 total, to try calendar rules through the ledger. See test-member clocks for record timestamps, replay, and real-time workflow scheduling.The exported
offsetDaysfield and broaderManagementClient.testMembers.advancemethod are optional for compatibility with existing clients. Factory clients always provideadvance. Shared records retain real timestamps, beneficiary attempts pin their projected activity, and history defaults to the member’s last fourteen days through member now.Test members also expose optional
today?: string(an ISO calendar date). Each advance captures a fixed duration shift, preserving timestamp order and freshness across DST. Evaluator tracker reads use that frozen shift; observation identity keeps raw timestamps.
1.3.0
Minor Changes
- 86114a5: Add
management.testMembers.history(member, { since }?)for Console sessions in the test environment. Read up to 500 attempts with ledger-credited activity times and 500 skipped tracker outcomes, with a completeness flag. The default window covers the last 14 days.
1.2.0
Minor Changes
-
7f69653: Add test-environment KV and D1 storage for Domino-hosted Worker apps through
relay.jsonapp.storage. Stores receive native Worker bindings and retain data when declarations are removed. SQL migration files run before staging activation, with Wrangler-compatible tracking and per-file rollback and safe retries after interrupted responses.Inspect declarations and retained stores with
domino storage list. Connected development and staging warn when local Wrangler bindings differ from hosted declarations. See hosted storage and migration recovery. -
cdea4a0: Browse hosted app storage read-only and delete stores no serving build declares.
domino storage tables,rows,keysandget, andmanagement.storagein the SDK, read SQL tables and KV values withpublish;domino storage deleteremoves an undeclared store and its data. See browse and delete stores.
1.1.1
Patch Changes
- 1d2a0cc: Recover saved submissions after the server permanently retires an operation. Preserve the terminal state across reloads, expose it for participant review, and allow explicit discard without silently submitting another claim. Starters show a separate restart action; unknown conflicts and lost responses still resume the original action. See the participant recovery reference.
1.1.0
Minor Changes
- ead35b5: Add Console-session-only management methods to create, list, act as, and reset test members in the test environment. Member reads expose the test marker and display name. Document the CLI exclusion and reset restrictions.
1.0.0
Major Changes
-
ad91102: Record prerequisite-only automatic decisions, expose participant and operator outcomes, and add previewed catch-up for paused events with review permission and audited reasons. Point-entry changes schedule current eligibility asynchronously after commit, including refunds and referral or leaderboard awards. Operators can inspect and retry failed scheduling.
Remove the permanent silent pause block. Outcome pagination now merges prerequisite and tracker decisions; reset saved cursors and use the
automaticidentity on rows without a tracker record. See the v1 upgrade guide. -
4b10080: Add the protocol-2
ctx.calendar.day(at?)evaluator helper for replay-safe project-calendar keys and day bounds. Protocol-1 bundles remain executable. See the v1 evaluator protocol upgrade guide. -
4205914: Add code-authored campaigns with half-open availability windows and durable campaign-wide enrollment; typed completion, live balance and project-group prerequisites; typed availability blockers; and live quest-budget capacity on participant views. Participant and management clients can enroll a member in a required campaign, and Console shows campaign rules and member enrollment.
completed(quest)now returns a discriminated completion prerequisite instead of{ quest }, andprerequisiteSchemaaccepts only typed prerequisite definitions. Stored legacy releases remain readable. See Prerequisites are typed eligibility rules in the v1 upgrade guide. -
6d66f96: Add reversible campaign enrollment. Participants can withdraw and re-enroll themselves; trusted servers and Console operators can withdraw a member with an audited reason and restore them. Withdrawal blocks new admissions while preserving the first enrollment time, admitted attempts, completions, and rewards.
-
a803b30: Evaluators now replay deterministically. Hosted evaluation freezes the clock for each attempt and seeds randomness from the attempt, so a retried or replayed attempt makes the same decision and
keyed()completion key. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.ctx.nowis new: the attempt's evaluation time in epoch milliseconds, the same on every run of the attempt.keyed()receives it too. It is distinct fromctx.eligibleAt, which is when the activity happened.Date.now(),new Date()andDate()returnctx.nowand do not advance, even across awaited capability calls.performance.now()returns 0.Math.random(),crypto.getRandomValues()andcrypto.randomUUID()repeat the same sequence for one attempt and differ between attempts.
Who should act. Evaluators that measure elapsed time or rely on unpredictable random values. Use
ctx.nowfor "as of evaluation" checks andctx.eligibleAtfor when the activity happened. See Evaluators see a frozen clock and seeded randomness in the v1 upgrade guide. -
e1cba88: Observations are removed, and every automatic activity arrives as a tracker record.
automatic({ source, type }),ctx.observation,management.observations.submit,POST /management/v1/observations, theObservationtype,observationSchema,observationInputSchema,observationReceiptSchema,observationTriggerSchema,discordReactionSchema,QuestView.trigger.observation(and the release trigger'sobservation) and theobservationfield of theautomaticavailability are gone.questStatusandrunsSilentlyfrom@domino-sdk/relay/reactnow label every tracker-triggered quest "Automatic", not only observation and keyed quests. A server that reported observations sends records to a custom tracker withmanagement.trackers.ingestand triggers quests withautomatic({ tracker }). Ingestion never joins a member to a project: a tracker trigger skips a member who has not joined withnot-joined. Quests published with an observation trigger leave the active set when Relay upgrades. Republish them from code on a tracker; the quest key can stay the same.Discord messages and reactions are two managed tracker sources,
discord.messages()(with{ text: true }to store message text) anddiscord.reactions(), on thediscordobject from@domino-sdk/relay/authoring. Domino's Discord gateway writes each new message or reaction into every tracker of that kind in the projects connected to the server, and keeps a record only when one of its accounts is a Discord account a member has linked. Their record data is typed (DiscordMessageData,DiscordMessageTextData,DiscordReactionData), sowherefilters andctx.tracker.record.dataare checked. A reaction record names the message's author as themessageAuthoraccount:automatic({ tracker: reactions, beneficiary: "messageAuthor" })replacesreaction.received, and Relay skips reactions to one's own message withbeneficiary-is-author. Discord trackers refuse ingestion (403) and refresh (422). Streaks work on these triggers.The
observepermission is removed. Ingesting needsingest, and enrolling a member from a server (management.quests.enroll) needs the newenrollpermission. Editors holdread,publish,inventory,points,ingestandenroll; hosted-app keys holdread,pointsandingestand cannot enroll. A personal API key issued withobservekeeps its other permissions, andobservegrants nothing: reissue it with Ingest or Enroll. Theobservation-questscapability is removed fromcapabilities.json; Discord tracker triggers are part of thetrackerscapability.To upgrade, see Observation triggers become tracker triggers and The
observepermission is replaced byenroll. -
23c85ba:
defineEngagementRewardtakesbudgets, a list of{ scope: "project" | "member", period: "day" | "week" | "month" | "lifetime", limit }, instead ofbudget: { projectDaily, memberDaily, projectLifetime }. Periods are those of the project calendar at settlement time, so a rule can now cap weekly or monthly payouts, and accounts settle once per project calendar day instead of once per UTC day. A definition withbudgetfails validation; rules already published keep their limits in the new form.See Engagement budgets are a list of periods in the v1 upgrade guide.
-
6cd0ba7: Make protocol 2 the first stable evaluator contract, including the required pinned project calendar. Remove pre-release protocol 1 from publication and execution, and preserve an SDK-built protocol-2 compatibility fixture. Rebuild and republish pre-v1 bundles during the approved cutover; changing only their protocol field is insufficient. See the evaluator protocol upgrade guide.
-
3a38259: Run content qualification and performance gates on custom content sources as well as X. Declare each custom source's body schema and metric names, use
content({ tracker }), and read the captured body throughctx.tracker.content. Content quests imply one completion per item. Replace retired content admission and approval calls with quest outcomes, claims and tracker outcome retries.Upgrade steps: Content stages.
-
4626004: Identity kinds are open. Besides
wallet,x,discordandmember, a custom tracker can declare your app's own kinds,app:<name>such asapp:telegram, and records name your app's user IDs. An identity integration created withidentityKind: "app:<name>"(set only at creation, kept by rotation) links each subject it signs in to the member as an identity of that kind, and tracker triggers attribute records through it, with{ provider: "app:<name>", subject, revision, linkedAt }asctx.tracker.attribution.account.A kind belongs to the integration that first claimed it, also after a disconnect, which keeps its links attributing. A new integration takes a kind that has identities only by adopting them:
adoptIdentitiesFrom(the old integration's ID, from the newmanagement.identityIntegrations.kinds()) with areasonthe audit log keeps; otherwise it is refused with 409. Reviewers unlink and relink a member's app identity (management.members.unlinkIdentityandrelinkIdentity, and Console's Tracker identities), each audited with a reason: unlinking stops attribution, ends the member's sessions for that subject, and refuses its sign-in with 409APP_IDENTITY_UNLINKED, whichcreateParticipantBackendpasses on, until it is relinked. A subject the integration already signs in as another member answers 409APP_IDENTITY_CONFLICT.Evaluators read the member's selected X and Discord accounts and app identities with
ctx.member.identities(), pinned once per attempt andnullwhen there are none. Management member reads return them asidentities, and Console lists them on the member under Tracker identities.Published tracker definitions in status, list, record-page, summary, and deployment responses use
trackerDefinitionResponseSchemaandTrackerDefinitionResponse. Authoring keeps the stricttrackerDefinitionSchemaandTrackerDefinition.Responses and evaluator contexts read identity kinds as an open set,
OpenIdentityKind, and ignore fields they do not know, so a kind a later Relay adds no longer fails them.Breaking:
IdentityKindis no longer a closed union,identityKindSchemais no longer az.enum, response kinds areOpenIdentityKind,trackerRecordSchemais a separate response schema,trackerAttributionSchema's account is a plain union with app and generic variants, andctx.memberis{ id, identities() }. See Identity kinds are open, and evaluators readctx.member.identities(). -
2149973: Read published trackers with future source kinds without rejecting a tracker list. Preserve unknown source payloads and expose
isKnownTrackerSourcefor supported-source narrowing. Tracker authoring remains closed. Update code that assumes every response source is supported as described in the v1 upgrade guide. -
54fcf41: The participant client follows one convention for paging, sessions and errors.
- Every method takes an optional last argument,
{ session?, signal? }.sessionreplaces the positional session argument ofwallets.challenge,wallets.verifyandme.collectionPass. Calls Relay binds to a session (wallet linking, collection passes,quests.retry) read the current session whensessionis omitted. - Every paged read takes
{ cursor? }and returns{ items, nextCursor }:referrals.history,quests.outcomes,content.qualifications,content.performance,engagement.pageandengagement.boundarieschanged frombefore,afterandnext. The participant routes answernextCursorand readcursor, and so do the management routes and client methods that share them (management.referrals.history(member, { cursor }),management.content.*,management.engagement.pageandboundaries). - Every rejection is a
RelayError, which extends the new sharedRequestError:kind(http,network,timeout,aborted,invalid-response,client),uncertain(true only for a write that may have taken effect: a failure after sending, a cut-off body, a 500, 502 or 504, or an unreadable success answer),statuswhenever response headers arrived, an open stringcode(known ones inRelayErrorCode),operationId, andrecovery(replaywith anoperationId, elsereconcile) when uncertain.isRelayErrorandisRequestErrorrecognize these errors across duplicate copies of the package.RelayError.statusis nownumber | undefined. Invalid input rejects withinvalid-inputinstead of aZodError, a success answer this SDK cannot read withinvalid-response, a body cut off after its headers with kindnetworkand its status, and a wallet signer's own failure withwallet-signer. The error body is read field by field, so Relay's message and a code the SDK does not know are always kept. Relay now sendscode: "not-signed-in"andcode: "stale-session"on those participant errors, and every paged read answers a malformed cursor with 400, codeinvalid-cursor, and one message. - Removed:
submit,retry(id),progress(member),upload(member, file),auth.connections,me.linkPass, thewallet.*namespace, and the staff calls the participant API refused (lookupMember,snapshot,publish,publishBatch,beginHandover,confirmHandover,decide,journeyTemplate,template). - Parameters take Zod input types, so defaulted fields are optional, and cursors are opaque strings the server validates. The session the client returns (
ParticipantSession) accepts fields and principal kinds a newer Relay adds;sessionMember(session)reads its member. - Hosted proxies no longer forward the 0.x single-wallet routes (
/v1/me/wallet,/challenge,/verify).
Upgrade: One paging, session and error convention in the participant client in the SDK v1 upgrade guide maps every method and has search commands.
- Every method takes an optional last argument,
-
c832384: Replace raw authoring resource keys with typed quest, ledger, tracker, and group handles. Authoring constructors and nested budget, tier, milestone, and bonus definitions now use
key; ledger declarations publish from modules; point-ledger settings return typed references. Stored releases and management, Console, and participant wire IDs remain compatible. See Authoring references use typed handles in the v1 upgrade guide. -
23c85ba: Each project environment has one project calendar: a timezone, UTC until you choose one, that sets where every day, week and month begins. Streak days, quest budgets, engagement budgets and calendar leaderboard windows all follow it, so a weekly budget and a weekly leaderboard cover the same week.
- Set it with
"calendar": { "timeZone": "Europe/Budapest" }in relay.json, in Console Settings → General → Project calendar, or with the newmanagement.calendar.read()andmanagement.calendar.configure({ timeZone, reason })(GETandPOST /calendar,publishpermission; in live only from Console). A change takes effect at the next midnight in the current timezone: the open day, week or month finishes in the old zone, and streaks stay consecutive. A deployment applies relay.json's timezone only when it differs from the last one deployed, and the deployment preview's newcalendarfield shows the change it schedules. streak()and calendar leaderboard windows no longer take atimeZone; both throw if given one. Streaks and boards already published with one still read.- Budgets reset at midnight in the project timezone instead of UTC, and accept
period: "month". - One validator,
timeZoneSchema, accepts named IANA zones only. Fixed offsets such as+02:00andEtc/GMT-2are rejected, including in leaderboards, which used to accept them. The sharedcalendarPeriodSchemaislifetime | day | week | month.
Who should act. Projects that pass
timeZonetostreak()or a calendar leaderboard window, and projects that rely on budgets resetting at UTC midnight. See Set the timezone once, on the project calendar in the v1 upgrade guide. - Set it with
-
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
-
c919948: Follow-ups to the React hooks' shared query layer.
completionCountLabel(view)is renamedcompletionCountText(view), so the name says it returns English likequestStatusText. For your own words, readview.completionCount.questStatus(view)returnsQuestStatus, whosecodeis the openQuestStatusCode, androleDeliveryStatus(deliveries, quest)returns the openRoleDeliveryStatus | null. Aswitchthat lists every code needs adefault, so a newer SDK can add codes without breaking the signature.useReferralSummary({ intervalMs })reads the summary again every 60 seconds by default while the page is visible, so friends who qualify show up without an app-side timer.useSignOut()drops the signed-out member's cached values as soon as Relay confirms the sign-out, even when the next session read fails.- A
useMyProfile()change that answers after another member signed in no longer writes into the new member's cache. useSignIn,useSignOut,useSignInOptions,useInvitationanduseQuestreturn objects and actions that keep their identity until something in them changes;useQuest's actions act on the latest view.useExperience().deliveriesis areadonly RoleDelivery[], and keeps one frozen empty list while there are none.progressOf(quests)is documented:totalcounts locked and paused quests and leaves out archived and repeatable ones.
Upgrade: React hooks share one cache and return one state shape in the SDK v1 upgrade guide.
-
c2390b3: The React hooks in
@domino-sdk/relay/reactshare one query cache and return one state shape.- Every read hook returns
{ status, data, error, refresh }:statusisloading,readyorerror, plussigned-outfor hooks that need a member;dataholds the value;erroris always aRelayError, and whilereadyit says why the latest refresh failed and the last value stays.useMeanduseSignInMethodsreturn this shape instead of a bare value,useMeno longer swallows errors, and the profile hooks report the uniform 404 aserrorwith status 404 instead ofmissing. <RelayProvider>keeps one keyed cache. Components that read the same thing share one request. A read that is replaced, invalidated, or belongs to a previous participant or client is cancelled, and its late answer is dropped. Polling, including the quest experience observer and the Discord role-delivery watcher, pauses while the tab is hidden and catches up when it is shown. Reads wait for the first session read, public ones included, and a sign-in attempt that does not change the session drops nothing.- Changes invalidate what they affect: every new completion the experience shows, submitted or automatic, reads balances, point history, leaderboards, profiles and referral progress again; an accepted invitation, the referral summary and referral progress; wallet, account and profile changes, their own reads. The
PROFILE_CHANGEDwindow event is removed.useInvalidate()andrelayKeysinvalidate by key prefix after a change made withuseRelay(). - The cache is scoped to the client, organization, project, environment, member and session. Signing in or out or switching accounts, projects or environments drops it, so one member never sees another's cached values. A new
clientorbaseUrlpassed toRelayProvidernow takes effect: the old observer stops, reads in flight are cancelled, and the hooks start from an empty cache. useSession()is new: who is signed in, independent of the experience read, so a failing/me/experienceno longer blanks the profile, wallet, account anduseMehooks.useExperience()returns the shared shape withdata.questsanddata.collectionsanddeliveries; itsmembermoved touseSession().Experienceis replaced byExperienceData, whichquestGroupstakes.useBalances()returns the{ items }answer indata, andusePointHistory(ledger)returnsdata.itemswithloadMore(); an unknown ledger or a member who has not joined iserrorwith codepoint-ledger-not-foundornot-joinedinstead of themissingandnot-joinedstatuses.BalancesStatebecomesMemberQueryandPointHistoryStatebecomesMemberPagedQuery.useRelayQuery,useMemberQuery,useRelayPagesanduseMemberPagesput an app's own reads on the same cache.Pageis the root package's type;PagedQuery(public pages) andMemberPagedQuery(member pages) addloadMore,loadingMoreandloadMoreError, which belong to one key and participant.- Every error the hooks report is a
RelayError:useQuest().error,useSignIn().error,useSignInOptions().errorandmethodsError,SignInMethods.error(also fromsignInOptions()in@domino-sdk/relay/browser), and a rejected or failed invitation'serror, which replacesreason. completionMark,hasPending,sanitizeDeliveries,watchRoleDeliveries,PENDING_POLL_MS,RETRY_DELAYS_MS,runsSilentlyandvisibleQuestsare no longer exported from@domino-sdk/relay/react.questStatus(view)returns{ tone, code, reason? }, a stable code to localize (QuestStatusCode, an open string likeRelayErrorCode);questStatusText(view)adds the Englishlabelanddetail.roleDeliveryStatus(deliveries, quest)returnspending,failed,grantedor null, androleDeliveryLabel(status)gives its English label.
Upgrade: React hooks share one cache and return one state shape in the SDK v1 upgrade guide maps every hook.
- Every read hook returns
-
1084288:
@domino-sdk/relaynow exports only the participant runtime client:createRelayClient,RelayError,QuestUpdatedError,formatUnits,parseUnits, and the types the client returns. Schemas, builders and avatar helpers moved to their own entry points, a few implementation helpers are no longer public, and two are no longer exported at all, so an app that imports the client no longer loads authoring builders, deployment and management schemas, or DiceBear.- Zod schemas (and helpers such as
identifier,caip10andxPostId) come from@domino-sdk/relay/schema. - Builders (
defineTracker,defineLeaderboard,defineReferral,defineEngagementReward,content,streak,settings,discord,pointLedgers,x,customSource) and evaluator reader types (TrackerReader,XReader,ImageReader) come only from@domino-sdk/relay/authoring. avatarSrc,defaultAvatar,defaultAvatarSvgandwalletAvatarSeedcome from@domino-sdk/relay/avatar, and no longer from@domino-sdk/relayor@domino-sdk/relay/react.- No longer public:
identityIntegrationSchema,identityIntegrationInputSchema,identityIntegrationSecretSchema,participantExchangeSchema,questConfiguration,parseImageResult,parseImageExtraction,isContentParticipantRoute,isProfileParticipantRoute,isWalletParticipantRouteandisWalletSignInRouteare exported only from@domino-sdk/relay/internal, which Relay's own services share and which is not a supported API. - No longer exported:
questActionsandreconcileQuestDraft. Open a quest controller withrelay.quests.open(view); itsquest-updatedresult already carries the reconcileddraft. - The package declares
sideEffects: false. esbuildis an optional peer dependency, needed only by@domino-sdk/relay/build.@domino-sdk/relay-clidepends on it directly, sodomino buildanddomino deploywork as before. Code that callsbuildQuestwithout the CLI must installesbuild.
Upgrade: Runtime entry exports only the client in the SDK v1 upgrade guide has the import mapping and search commands.
- Zod schemas (and helpers such as
-
5d4ec3e: Release Relay SDK and CLI 1.0.0. Install the SDK and CLI together at the same version. This release removes pre-v1 APIs; follow the SDK v1 upgrade guide before replacing a 0.x installation.
Stable APIs follow semantic versioning throughout the supported v1 lifetime. Compatibility exemptions apply only to explicitly experimental entrypoints.
-
ae047ca: Remove the pre-v1 runtime, release, single-wallet and tracker compatibility shims. Published quests must carry an explicit evaluator protocol; protocol 2 is the first stable contract, and all pre-v1 bundles must be rebuilt and republished. Participant and management reads now expose only canonical wallet lists, and tracker policies use canonical identity roles and an explicit observation mode. See Wallet reads return every linked wallet in the v1 upgrade guide.
-
bdb89a4: Evaluator readers follow one rule: a missing subject is
null, a failure throwsCapabilityError, linked accounts are pinned once per evaluation run, and tracker reads are point in time. Retries replay the same run; scheduled deferred evaluations get fresh captures. Pre-v1 releases must be rebuilt and republished during cutover.- Missing is
null.ctx.x.profile(),follows()andreposted()returnnullwithout a linked X account instead of throwing;ctx.x.post()returnsnullfor a post that does not exist and needs no linked account.ctx.discord.member()returnsnullwithout a linked Discord account.ctx.wallet.list()returnsnullinstead of[], andctx.wallet.balance()returnsnullinstead oftotal: "0"when the member has no wallet of that family.ctx.trackers.content()returnsnullfor a missing record or observation. See Readers returnnullwhen an account is not linked or a subject is missing. - Failures throw
CapabilityError, new in@domino-sdk/relay/authoring(its wire shape,capabilityFailureSchema, is in@domino-sdk/relay/schema), withcode("not-linked","provider-unavailable","author-error") andretryable. Invalid capability input isauthor-error; an image check of{ x: "profile" }without a linked X account isnot-linked. Failures that are not retryable are captured and replay. A retryable failure that quest code handles and moves past is final for that evaluation run; one that escapes the evaluation is released, along with every fact captured after it, and retried, so the retry asks the provider and the later calls again. Rethrow a caught failure, or wrap it with{ cause: error }, to let it be retried. The failure shape is open: a newer Relay can add codes, and the SDK treats an unknown code as not retryable. See Capability failures throwCapabilityError. - Tracker reads are point in time.
ctx.trackers.records()andsummary()takeasOf, defaulting toctx.eligibleAt, and every tracker read of an evaluation run sees the observations stored at its first tracker read. PassasOf: ctx.nowto count everything stored by then. See Tracker reads are point in time and pinned per attempt. - Linked accounts stay pinned. X and Discord readers pin the account on the evaluation run's first read and replay that pin on retries, as wallets already did, so a link change no longer fails the attempt with "X account changed" or "Account or integration changed". See Linked X and Discord accounts stay pinned for the attempt.
Who should act. Evaluators that read
ctx.x,ctx.discord,ctx.walletorctx.trackers, or that match capability error messages. The changed return types point TypeScript at each read to update. Thediscord-membership,token-holderandcommunity-screenshotsnippets and the Discord starter'sjoin-the-serverquest are updated. - Missing is
Minor Changes
-
afe1128: Operators can revoke an accepted completion with a reason:
management.completions.revoke({ completion, reason, freeOccurrence })(POST /management/v1/completions/<id>/revoke,reviewpermission). In one transaction the completion stops counting (progress, prerequisites of new attempts, counts, profiles and referral qualification), its point awards are reversed as far as the member's available points allow, tiered rewards it granted are voided or lowered to the tier another completion grants (collected ones are flagged), Discord role grants not yet delivered are stopped while delivered roles are flagged for the operator to remove in Discord (Relay never removes roles; automatic removal is DOM-99), referral programs that qualified on it are re-evaluated, and a streak milestone it paid is revoked while its days stay.freeOccurrencelets the member complete the quest again for the same completion key; by default later attempts for it are rejected. The result reports each part of the cascade. API keys withreviewpermission can revoke in live. Console offers it on member detail and accepted attempts, and the attempt history and Audit history show the revoke and its reason.domino capabilities --jsonnow markscompletion-revokeas native.Breaking: a reward delivery's
actionis an open string (RewardDeliveryActionlists"grant-role"), the worker fails a delivery whose action it does not know, and a grant still pending at the revoke fails. The revoke result'soutcomefields are open strings with known values inRevokedEntitlementOutcome,RevokedDeliveryOutcomeandRevokedReferralOutcome. To upgrade, see Reward delivery actions are open strings.Revoking preserves percentage bonuses from other valid completions and queues a new automatic attempt when a freed occurrence still meets its prerequisites. Delivery responses add optional
dispatchedAtandrevokedAtfields. Dispatch history survives retries, revoke fences failed generations, and late provider success stays visible without reopening delivery. Console uses the revoke field to stop retries and shows unknown delivered actions by their generic status.Revocation refuses missing or unreadable grant history before changing member state. Collected entitlements carry a persistent recovery flag in ordinary reads and Console. Correcting collection clears the flag and voids an unearned reward instead of reopening it; a surviving grant reopens only its earned tier. Reused-completion attempt timelines include the shared completion’s revoke event.
-
b0bffa0: Add native distinct-action progress counters with calendar and rolling windows, durable achievements, participant summaries and audited operator correction. Participant state follows settlement order for late-arriving repeat batches, and achievement alerts remain scoped to their calendar window when the next window reopens. Add qualified-referral and continuous-membership examples. See counter rules. Update custom attempt renderers using the counter upgrade steps.
-
bac9ca4: Declare provider modules to enable evaluator readers, use
discord.grantRole()for role rewards, and replace Solana-only wallet readers withwallet.list()andwallet.balance(). See the upgrade guide. -
e7f81ff: Add signed outbound webhooks for operator backends, with scoped subscriptions, bounded retries, delivery logs, replay, and overlapping secret rotation. Expose
management.webhooks,verifyWebhook, shared activity constants, anddomino webhookscommands. This is additive; existing integrations need no changes. See Receive outbound webhooks for an optional polling migration. -
8465d40: Operators can correct a member's tiered reward, at the revision they saw and with an audited reason.
management.handover.cancel(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/cancel,handoverpermission) returns a handover in progress toavailableand puts its pickup size back into stock.management.entitlements.correctCollection(…/correct,review) does the same for a collection recorded by mistake, andmanagement.entitlements.void(…/void,review) voids an available entitlement. A stale revision fails with 409entitlement-changed, the wrong starting status with 409entitlement-status. API keys with the permission can make these corrections in live. Console Rewards and member detail offer them, and Audit history lists each one with its reason.domino capabilities --jsonlists them asentitlement-corrections.Breaking:
Entitlementgains the status"void", withvoidedAt. A voided entitlement cannot be collected; a quest that grants the same reward to the member again makes itavailable, as a fresh grant at that quest's tier; a voided referral reward stays void. SDKs from before this release fail the whole progress read once a member has a voided entitlement. Reads now parse entitlements with a tolerantentitlementSchema(fields a newer server adds are kept, and an unknown status reads as"unsupported"withreportedStatus); the strict stored shape isentitlementStateSchema, and the management client'sentitlementRecordSchemais removed in favor ofentitlementSchema. To upgrade, see Entitlements can bevoid, and reads tolerate statuses they do not know. -
84feaf7: Declare evaluator protocol 2 on authored bundles and descriptions, including the pinned project calendar. The host selects a versioned adapter, derives readers from the capability registry and preserves captured capability requests for replay. Custom bundle builders must keep both protocol declarations. See the v1 evaluator protocol upgrade guide.
-
71cf912: Add reviewable leaderboard settlements. Fixed and calendar earned/net leaderboards can declare point and tiered-reward prizes, choose shared-rank or earliest-score tie handling, and freeze each ended window with its eligibility and exclusions at cutoff. Entries received after the cutoff stay out even when backdated, and a definition with a changed window advances past an obsolete scheduled cutoff. Operators review and approve delivery in Console or with
management.leaderboards, where the approval reason remains visible, and can correct an individual winner through completion revocation without awarding that settlement again. -
f2f1b75: Breaking: the management client moves from
@domino-sdk/relay-clito the new@domino-sdk/relay/serverentrypoint, withcreateManagementClient,hostedManagementClient,ManagementError, the management schemas and types, andstarterTemplates. Server code, including a hosted app's Worker, now needs only@domino-sdk/relay, without the CLI's command-line parser and TypeScript compiler.@domino-sdk/relay-cliis only thedominoexecutable and its agent skills; it has nothing to import.serverBoundary()also fails a browser build that imports@domino-sdk/relay/server. To upgrade, see Import the management client from@domino-sdk/relay/server.The capability catalog (
domino capabilities --json) adds staff confirmation, staff handover, stock, point corrections, deploy and stage, operator member reads and lookup, project access, and the management API withdomino api. It marks webhooks, bulk data export, completion revocation and tracker-scoped keys as unsupported, and lists the management methods it leaves out on purpose inexcludedManagementMethods. -
8c06912: Add operator reads and audited unlink for current X and Discord quest accounts. Reviewers provide the subject and selection revision to prevent a stale review from unlinking a newer connection. Identity ownership, sign-in grants and existing rewards remain unchanged. Console exposes the action on each member's Quest accounts section.
-
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
710e520: Participants read their own point balances and history.
relay.me.balances()(GET /v1/me/points) returns{ items }: the signed-in member'sbalanceandavailablepoints (after reservations) in every ledger of the project, each with itsledgerreference.relay.me.pointHistory({ ledger, cursor? })(GET /v1/me/points/:ledger/history) pages through the member's own entries in one ledger, by key or ID, most recently recorded first, as{ items, nextCursor }. Each entry is{ id, amount, kind, at, quest, reverses }, without the operator's reason or actor.kindis an open string; thePointEntryKindconstants (award,spend,adjust,reverse) name the kinds this SDK knows, and referral payouts areadjust. An unknown ledger answers 404 with codepoint-ledger-not-foundand a member who has not joined the project 404 withnot-joined, both added toRelayErrorCode. Neither read takes a member, and both are scoped to the session's project and environment. React apps useuseBalances(), which reads again after a quest completes, andusePointHistory(ledger). The participant proxy and identity integrations admit both routes. New schemas and types:pointBalanceSchema,pointBalancesSchema,pointEntrySchema,pointHistoryQuerySchema,pointHistoryPageSchema,PointBalance,PointBalances,PointEntry,PointEntryKind,KnownPointEntryKind,PointHistoryQueryandPointHistoryPage.The classic starter's header now reads the balance with
useBalances()instead of its/api/me/pointsserver route, which is gone. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
-
c320586: A content quest's
performance.evaluatecan no longer select rewards. It gates the rewards the content evaluation already selected, and it always did: arewardsvalue in its result was silently ignored. Its type is nowPerformanceOutcome(accept,revieworrejectwith a reason only), sodecision.accept({ rewards })anddecision.review({ reason, rewards })are type errors there, and a runtime result that carriesrewardsfails the evaluation with "A performance evaluator cannot select rewards". To change what a post earns, select rewards in the quest'sevaluate.See Performance evaluators no longer select rewards in the upgrade guide.
-
c320586: Replace untyped prerequisite objects with typed rules.
completed(questHandle)creates a completion rule withscope: "ever";completed(questHandle, { scope: "period", period: "day" })checks the current project-calendar period. Balance and group requirements usebalanceAtLeast(ledgerHandle, minimum)andinGroup(groupHandle). Persisted rules carry an explicitkind; bare{ quest }and string prerequisites are not canonical v1 input.See Prerequisites are typed eligibility rules and Authoring references use typed handles in the upgrade guide.
-
569989e: Reconcile Discord role membership across projects and environments, preserve external assignments, and expose operator recovery plus current membership alongside delivery history. Revoking the last accepted grant now removes Relay-owned assignments. Read the SDK v1 upgrade guide before upgrading.
Treat forbidden Discord membership reads as permanent delivery failures so operators can fix permissions and retry immediately.
Publish initial pending delivery events in the grant transaction so observers can see queued rewards before reconciliation starts.
-
8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
-
11ab1d8: Add explicit archive and restore for code-declared resources through the management client, durable CLI operations and Console. Keep resource identities and history while stopping new activity, and preserve archive state across publication. See the resource lifecycle.
-
ddfc526: The participant client, the management client and the CLI share one set of deadline, cancellation and retry rules.
- Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
createRelayClient({ timeout })and the new per-call{ timeout }option of every participant method set it, astimeoutalready does for the management client. A timeout is never retried. - A call is sent again, at most three times in all, only when the server applies a repeat at most once: reads; participant writes with an action ID, submission evidence by its upload ID,
wallets.verify(by its challenge) andwallets.challenge, but not avatar uploads, whose upload ID has no receipt; typed management writes whose input has anactionId; and the CLI'sdeploypublication,stagebuild request andquests archive/unarchive. Every attempt sends the same payload and session. Retries follow a lost answer, including a body that breaks off after successful headers, and HTTP 500, 502, 503 and 504, with a short jittered backoff. Invalid or unexpected answers, cancellations, rejected input, other refusals and every other write are sent once. - A 429 is retried only when its
Retry-Afterfits in the time left, and every wait honoursRetry-After. The newretryAfterfield ofRequestError(and soRelayErrorandManagementError) carries the wait the server asked for, in milliseconds; the CLI reports it asdetails.retryAfteronRATE_LIMITEDandSERVER_ERROR. - A session-bound participant call that reads the current session first shares one deadline with that read, and
auth.session()honourstimeout. - An error status whose body breaks off, times out or is cancelled is now a
network,timeoutorabortedfailure that keeps itsstatusandretryAfter, in all three clients; an error body that is not JSON stayshttp. - Cancelling with
signalstops a retry wait at once. A write any attempt of which may have been applied staysuncertain, with itsoperationIdandrecovery, whatever later attempts answer. management.requestretries only GET, andmanagement.rawsends once.useInvitation()keeps a rate-limited code for a retry instead of discarding it as refused.
Avatar failures report
recovery: "reconcile"even when they have an upload ID. Wallet address and signature waits reject when the caller cancels. See Bounded requests and avatar recovery for the recovery-handler change.Generic management writes report
recovery: "reconcile"even with a caller-supplied action ID. Uncertaindomino apiwrites exposedetails.recovery: "reconcile". See Generic management writes require reconciliation for the recovery-handler change. - Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
-
89ea6ba: Add reviewed live project publication. The management client can list deployment records, submit and reject previews, promote a published test deployment, and prepare a rollback to the previous live deployment. Promotion and rollback accept an action UUID, and the management client generates one when omitted so retries return the same deployment record.
domino deploy --environment livenow returnsstatus: "pending", and--promotesubmits test source for live review; only a person in Console can approve, reject, or prepare rollback. The newpendingdeploy status is a breaking addition for exhaustive automation. See Live deploys return a pending review. -
67d0d8c: Add bounded
decision.deferand absolutedecision.progressresults, fresh evaluation runs scheduled by durable timers, participant progress and deferred status, and consistent completion-key authoring types. Reviewers can reject a deferred attempt to stop it.Attempt response statuses accept future server values; use known
AttemptStatusconstants with a fallback. New manual submissions while deferred return409 attempt-pending. Custom decision/status switches must handle the new variants. See Handle deferred evaluator decisions and progress for old and new code, capture semantics, and search patterns. -
d3b6261: Restrict personal ingestion keys to selected custom trackers, inspect and edit their scope without changing the secret, and return TRACKER_SCOPE_DENIED for disallowed writes. Human CLI sign-ins can manage their own keys; project-scoped playground sessions and API keys cannot. Existing unrestricted keys retain project-wide ingestion.
-
d7f9fe4:
streak(...)completes manual and tracker-triggered quests. Each accepted attempt counts the project-calendar day of its trusted occurrence time, exposed asctx.eligibleAt: a tracker observation'soccurredAt, or the admission time of a manual submission. A rejected manual submission preserves committed streak progress and displays its reason. Once the streak milestone pays, later tracker observations do not pay it again.Content-stage quests and prerequisite-only
automatic()quests cannot use streak completion. A published quest cannot change between manual and tracker triggers under the same key. Observation triggers are removed; migrate them to trackers. See Observation triggers become tracker triggers. -
0a89168: Operators can archive point ledgers and correct points from Console member detail.
management.pointLedgers.list()includes nullablearchivedAt, andmanagement.pointLedgers.archive(id)archives a ledger. Archived ledgers remain in balance and history reads and existing published rewards keep working, but new reward pickers and published definitions reject them. -
cf5b02c: Custom tracker records have a declared data schema.
customSource(namespace, { data })requires a zod object schema (zis exported by@domino-sdk/relay/authoring), stores its JSON Schema form with the tracker, and throws, naming the field, for what ingestion cannot check (string formats and patterns, tuples, refinements, transforms, dates, bigints). Publication refuses a custom tracker without one. Ingestion rejects a batch whose record data does not match with 422 and{ error, code: "invalid-record-data", record, path, reason }, one too costly to validate withrecord-data-too-complex, and records for a tracker published without a schema withtracker-schema-required. Schemas are bounded (32 KB, 16 levels, 256 properties, required keys, enum values or key names per node, 16 branches per union, no wide union nested in another union's branch), and validation charges every unit of work to a per-record and per-batch step budget. A deployment whose new schema breaks a live quest'swherefilter, Console quests included, is refused. Stored records are not rechecked: tracker triggers skip non-matching records with the new skip codeinvalid-record-data, evaluators reject pinned records that do not match the schema the quest was built with, andtrackers.records(handle)leaves them out and counts them inexcluded. The schema can change under the same tracker key and applies to later records.The data type flows into quests:
ctx.trackeris non-null in a tracker-triggered quest's evaluator withrecord.dataof the tracker's type (content triggers expose their captured body throughctx.tracker.content),trackers.records(tracker)returns typed records, andwherepaths,equalsvalues and{ setting }bounds are type-checked against the data and the quest's integer settings.wherealso accepts a list of conditions that must all hold, andautomatic()and publication check filter paths against the stored schema.settings.objectanddefineQuestaccept onlysettings.*builders; a plain zod setting is a type error anddefineQuestthrowsSetting "<key>" is not a quest setting.See Custom sources declare their record data and Quest settings come only from settings builders in the upgrade guide.
-
1e23d8f: Type quest completion and prerequisites from the trigger, preserve source-specific beneficiary roles, and share shape validation with publication. Reusable prerequisite-only types can receive prerequisites from their instances. See the upgrade guide.
Patch Changes
- ed4988c: Read Cosmos Hub and Osmosis bank balances with
ctx.wallet.balance(), pinned to one verified block and the member's wallets on the exact Cosmos network. - c320586:
defineQuestthrows at build time for two shapes that only failed when publishing: a bareautomatic()quest with norequiresentry, and a bareautomatic()quest withkeyed()completion. A bareautomatic()runs once its prerequisites are complete, so it needs at least one typed completion, balance or group prerequisite and completes once. The backend still checks both when it publishes. - 1141af4: Fix invalid action IDs in the tracker and leaderboard documentation examples. Validate action ID literals during documentation checks so copied examples use the identifier format the API accepts.
- 83c4426: Update Discord message tracker records when a message is edited or deleted. Turning text off or deleting a message now erases retained message text.
- c320586:
manual({ actor: "staff", input })throws instead of dropping the actor. Its types never allowed the combination, but untyped callers got a quest that members could submit evidence to, not one staff confirm. Staff-captured evidence is not supported: usemanual({ actor: "staff" })for a staff confirmation, ormanual({ input })for evidence the participant submits. - 7e37049: The SDK's optional React peer dependency is now
^19.0.0instead of>=19, matching the React versions the React hooks are verified against. React is still optional: the runtime, browser and server entrypoints do not need it. Apps on React 19 need no change. - 909f343: The browser submission store recovers from storage failures instead of caching them. A temporary IndexedDB opening error, a blocked open, a
versionchangefrom another tab, or a connection the browser closes no longer fails every later call on the same client: the nextstatus(),resume()orsubmit()opens storage again, and the saved operation and its operation ID are kept. A blocked open waits briefly for the other tab, then rejects withsubmission-storage, and the pending request is adopted if it later succeeds. Full browser storage rejects with the newRelayErrorCode.SubmissionStorageQuota(submission-storage-quota); an upgrade by a newer page asks for a reload. Both areclienterrors that are notuncertain, and a failed read never looks like an empty store, so a new claim is never started over a saved one. See Storage failures in the participant API reference. - 8b706af: An attempt now records when its evaluator asked for human review on a quest with review off.
decision.review({ reason })was already a rejection with the review reason in that case. The attempt now also hasrequestedReview: true, and Console attempt detail says "Would have gone to review; human review is off". A reject the evaluator returned itself keepsrequestedReview: false. Thereviewoption, the quests concept page and the authoring reference describe it. - e1e701b: Read added trigger and availability fields in participant progress and quest views without weakening publication input validation. Verify unknown delivery providers, actions and statuses through the wire compatibility fixtures.
1.0.0-next.34
Major Changes
-
ad91102: Record prerequisite-only automatic decisions, expose participant and operator outcomes, and add previewed catch-up for paused events with review permission and audited reasons. Point-entry changes schedule current eligibility asynchronously after commit, including refunds and referral or leaderboard awards. Operators can inspect and retry failed scheduling.
Remove the permanent silent pause block. Outcome pagination now merges prerequisite and tracker decisions; reset saved cursors and use the
automaticidentity on rows without a tracker record. See the v1 upgrade guide. -
4b10080: Add the protocol-2
ctx.calendar.day(at?)evaluator helper for replay-safe project-calendar keys and day bounds. Protocol-1 bundles remain executable. See the v1 evaluator protocol upgrade guide. -
4205914: Add code-authored campaigns with half-open availability windows and durable campaign-wide enrollment; typed completion, live balance and project-group prerequisites; typed availability blockers; and live quest-budget capacity on participant views. Participant and management clients can enroll a member in a required campaign, and Console shows campaign rules and member enrollment.
completed(quest)now returns a discriminated completion prerequisite instead of{ quest }, andprerequisiteSchemaaccepts only typed prerequisite definitions. Stored legacy releases remain readable. See Prerequisites are typed eligibility rules in the v1 upgrade guide. -
6d66f96: Add reversible campaign enrollment. Participants can withdraw and re-enroll themselves; trusted servers and Console operators can withdraw a member with an audited reason and restore them. Withdrawal blocks new admissions while preserving the first enrollment time, admitted attempts, completions, and rewards.
-
6cd0ba7: Make protocol 2 the first stable evaluator contract, including the required pinned project calendar. Remove pre-release protocol 1 from publication and execution, and preserve an SDK-built protocol-2 compatibility fixture. Rebuild and republish pre-v1 bundles during the approved cutover; changing only their protocol field is insufficient. See the evaluator protocol upgrade guide.
-
3a38259: Run content qualification and performance gates on custom content sources as well as X. Declare each custom source's body schema and metric names, use
content({ tracker }), and read the captured body throughctx.tracker.content. Content quests imply one completion per item. Replace retired content admission and approval calls with quest outcomes, claims and tracker outcome retries.Upgrade steps: Content stages.
-
c832384: Replace raw authoring resource keys with typed quest, ledger, tracker, and group handles. Authoring constructors and nested budget, tier, milestone, and bonus definitions now use
key; ledger declarations publish from modules; point-ledger settings return typed references. Stored releases and management, Console, and participant wire IDs remain compatible. See Authoring references use typed handles in the v1 upgrade guide. -
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
-
ae047ca: Remove the pre-v1 runtime, release, single-wallet and tracker compatibility shims. Published quests must carry an explicit evaluator protocol; protocol 1 remains supported, while protocol-less bundles must be republished. Participant and management reads now expose only canonical wallet lists, and tracker policies use canonical identity roles and an explicit observation mode. See Wallet reads return every linked wallet in the v1 upgrade guide.
Minor Changes
-
b0bffa0: Add native distinct-action progress counters with calendar and rolling windows, durable achievements, participant summaries and audited operator correction. Participant state follows settlement order for late-arriving repeat batches, and achievement alerts remain scoped to their calendar window when the next window reopens. Add qualified-referral and continuous-membership examples. See counter rules. Update custom attempt renderers using the counter upgrade steps.
-
bac9ca4: Declare provider modules to enable evaluator readers, use
discord.grantRole()for role rewards, and replace Solana-only wallet readers withwallet.list()andwallet.balance(). See the upgrade guide. -
84feaf7: Declare evaluator protocol 1 on authored bundles and descriptions. The host selects a versioned adapter, derives readers from the capability registry and preserves captured capability requests for replay. Custom bundle builders must keep both protocol declarations. See the v1 evaluator protocol upgrade guide.
-
71cf912: Add reviewable leaderboard settlements. Fixed and calendar earned/net leaderboards can declare point and tiered-reward prizes, choose shared-rank or earliest-score tie handling, and freeze each ended window with its eligibility and exclusions at cutoff. Entries received after the cutoff stay out even when backdated, and a definition with a changed window advances past an obsolete scheduled cutoff. Operators review and approve delivery in Console or with
management.leaderboards, where the approval reason remains visible, and can correct an individual winner through completion revocation without awarding that settlement again. -
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
-
8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
-
11ab1d8: Add explicit archive and restore for code-declared resources through the management client, durable CLI operations and Console. Keep resource identities and history while stopping new activity, and preserve archive state across publication. See the resource lifecycle.
-
0a89168: Operators can archive point ledgers and correct points from Console member detail.
management.pointLedgers.list()includes nullablearchivedAt, andmanagement.pointLedgers.archive(id)archives a ledger. Archived ledgers remain in balance and history reads and existing published rewards keep working, but new reward pickers and published definitions reject them. -
1e23d8f: Type quest completion and prerequisites from the trigger, preserve source-specific beneficiary roles, and share shape validation with publication. Reusable prerequisite-only types can receive prerequisites from their instances. See the upgrade guide.
Patch Changes
- ed4988c: Read Cosmos Hub and Osmosis bank balances with
ctx.wallet.balance(), pinned to one verified block and the member's wallets on the exact Cosmos network. - e1e701b: Read added trigger and availability fields in participant progress and quest views without weakening publication input validation. Verify unknown delivery providers, actions and statuses through the wire compatibility fixtures.
1.0.0-next.33
Minor Changes
- 89ea6ba: Add reviewed live project publication. The management client can list deployment records, submit and reject previews, promote a published test deployment, and prepare a rollback to the previous live deployment. Promotion and rollback accept an action UUID, and the management client generates one when omitted so retries return the same deployment record.
domino deploy --environment livenow returnsstatus: "pending", and--promotesubmits test source for live review; only a person in Console can approve, reject, or prepare rollback. The newpendingdeploy status is a breaking addition for exhaustive automation. See Live deploys return a pending review.
1.0.0-next.32
Minor Changes
-
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
1.0.0-next.31
Minor Changes
-
569989e: Reconcile Discord role membership across projects and environments, preserve external assignments, and expose operator recovery plus current membership alongside delivery history. Revoking the last accepted grant now removes Relay-owned assignments. Read the SDK v1 upgrade guide before upgrading.
Treat forbidden Discord membership reads as permanent delivery failures so operators can fix permissions and retry immediately.
Publish initial pending delivery events in the grant transaction so observers can see queued rewards before reconciliation starts.
1.0.0-next.30
Minor Changes
-
afe1128: Operators can revoke an accepted completion with a reason:
management.completions.revoke({ completion, reason, freeOccurrence })(POST /management/v1/completions/<id>/revoke,reviewpermission). In one transaction the completion stops counting (progress, prerequisites of new attempts, counts, profiles and referral qualification), its point awards are reversed as far as the member's available points allow, tiered rewards it granted are voided or lowered to the tier another completion grants (collected ones are flagged), Discord role grants not yet delivered are stopped while delivered roles are flagged for the operator to remove in Discord (Relay never removes roles; automatic removal is DOM-99), referral programs that qualified on it are re-evaluated, and a streak milestone it paid is revoked while its days stay.freeOccurrencelets the member complete the quest again for the same completion key; by default later attempts for it are rejected. The result reports each part of the cascade. API keys withreviewpermission can revoke in live. Console offers it on member detail and accepted attempts, and the attempt history and Audit history show the revoke and its reason.domino capabilities --jsonnow markscompletion-revokeas native.Breaking: a reward delivery's
actionis an open string (RewardDeliveryActionlists"grant-role"), the worker fails a delivery whose action it does not know, and a grant still pending at the revoke fails. The revoke result'soutcomefields are open strings with known values inRevokedEntitlementOutcome,RevokedDeliveryOutcomeandRevokedReferralOutcome. To upgrade, see Reward delivery actions are open strings.Revoking preserves percentage bonuses from other valid completions and queues a new automatic attempt when a freed occurrence still meets its prerequisites. Delivery responses add optional
dispatchedAtandrevokedAtfields. Dispatch history survives retries, revoke fences failed generations, and late provider success stays visible without reopening delivery. Console uses the revoke field to stop retries and shows unknown delivered actions by their generic status.Revocation refuses missing or unreadable grant history before changing member state. Collected entitlements carry a persistent recovery flag in ordinary reads and Console. Correcting collection clears the flag and voids an unearned reward instead of reopening it; a surviving grant reopens only its earned tier. Reused-completion attempt timelines include the shared completion’s revoke event.
1.0.0-next.29
Minor Changes
-
67d0d8c: Add bounded
decision.deferand absolutedecision.progressresults, fresh evaluation runs scheduled by durable timers, participant progress and deferred status, and consistent completion-key authoring types. Reviewers can reject a deferred attempt to stop it.Attempt response statuses accept future server values; use known
AttemptStatusconstants with a fallback. New manual submissions while deferred return409 attempt-pending. Custom decision/status switches must handle the new variants. See Handle deferred evaluator decisions and progress for old and new code, capture semantics, and search patterns. This ships in the1.0.0-nextprerelease series. -
d3b6261: Restrict personal ingestion keys to selected custom trackers, inspect and edit their scope without changing the secret, and return TRACKER_SCOPE_DENIED for disallowed writes. Human CLI sign-ins can manage their own keys; project-scoped playground sessions and API keys cannot. Existing unrestricted keys retain project-wide ingestion.
1.0.0-next.28
Patch Changes
- 83c4426: Update Discord message tracker records when a message is edited or deleted. Turning text off or deleting a message now erases retained message text.
1.0.0-next.27
Minor Changes
- e7f81ff: Add signed outbound webhooks for operator backends, with scoped subscriptions, bounded retries, delivery logs, replay, and overlapping secret rotation. Expose
management.webhooks,verifyWebhook, shared activity constants, anddomino webhookscommands. This is additive; existing integrations need no changes. See Receive outbound webhooks for an optional polling migration.
1.0.0-next.26
Minor Changes
- 8c06912: Add operator reads and audited unlink for current X and Discord quest accounts. Reviewers provide the subject and selection revision to prevent a stale review from unlinking a newer connection. Identity ownership, sign-in grants and existing rewards remain unchanged. Console exposes the action on each member's Quest accounts section.
1.0.0-next.25
No changes in this release.
1.0.0-next.24
Major Changes
-
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
1.0.0-next.23
Major Changes
- 2149973: Read published trackers with future source kinds without rejecting a tracker list. Preserve unknown source payloads and expose
isKnownTrackerSourcefor supported-source narrowing. Tracker authoring remains closed. Update code that assumes every response source is supported as described in the v1 upgrade guide.
1.0.0-next.22
Major Changes
-
4626004: Identity kinds are open. Besides
wallet,x,discordandmember, a custom tracker can declare your app's own kinds,app:<name>such asapp:telegram, and records name your app's user IDs. An identity integration created withidentityKind: "app:<name>"(set only at creation, kept by rotation) links each subject it signs in to the member as an identity of that kind, and tracker triggers attribute records through it, with{ provider: "app:<name>", subject, revision, linkedAt }asctx.tracker.attribution.account.A kind belongs to the integration that first claimed it, also after a disconnect, which keeps its links attributing. A new integration takes a kind that has identities only by adopting them:
adoptIdentitiesFrom(the old integration's ID, from the newmanagement.identityIntegrations.kinds()) with areasonthe audit log keeps; otherwise it is refused with 409. Reviewers unlink and relink a member's app identity (management.members.unlinkIdentityandrelinkIdentity, and Console's Tracker identities), each audited with a reason: unlinking stops attribution, ends the member's sessions for that subject, and refuses its sign-in with 409APP_IDENTITY_UNLINKED, whichcreateParticipantBackendpasses on, until it is relinked. A subject the integration already signs in as another member answers 409APP_IDENTITY_CONFLICT.Evaluators read the member's selected X and Discord accounts and app identities with
ctx.member.identities(), pinned once per attempt andnullwhen there are none. Management member reads return them asidentities, and Console lists them on the member under Tracker identities.Published tracker definitions in status, list, record-page, summary, and deployment responses use
trackerDefinitionResponseSchemaandTrackerDefinitionResponse. Authoring keeps the stricttrackerDefinitionSchemaandTrackerDefinition.Responses and evaluator contexts read identity kinds as an open set,
OpenIdentityKind, and ignore fields they do not know, so a kind a later Relay adds no longer fails them.Breaking:
IdentityKindis no longer a closed union,identityKindSchemais no longer az.enum, response kinds areOpenIdentityKind,trackerRecordSchemais a separate response schema,trackerAttributionSchema's account is a plain union with app and generic variants, andctx.memberis{ id, identities() }. See Identity kinds are open, and evaluators readctx.member.identities().
1.0.0-next.21
Minor Changes
-
ddfc526: The participant client, the management client and the CLI share one set of deadline, cancellation and retry rules.
- Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
createRelayClient({ timeout })and the new per-call{ timeout }option of every participant method set it, astimeoutalready does for the management client. A timeout is never retried. - A call is sent again, at most three times in all, only when the server applies a repeat at most once: reads; participant writes with an action ID, submission evidence by its upload ID,
wallets.verify(by its challenge) andwallets.challenge, but not avatar uploads, whose upload ID has no receipt; typed management writes whose input has anactionId; and the CLI'sdeploypublication,stagebuild request andquests archive/unarchive. Every attempt sends the same payload and session. Retries follow a lost answer, including a body that breaks off after successful headers, and HTTP 500, 502, 503 and 504, with a short jittered backoff. Invalid or unexpected answers, cancellations, rejected input, other refusals and every other write are sent once. - A 429 is retried only when its
Retry-Afterfits in the time left, and every wait honoursRetry-After. The newretryAfterfield ofRequestError(and soRelayErrorandManagementError) carries the wait the server asked for, in milliseconds; the CLI reports it asdetails.retryAfteronRATE_LIMITEDandSERVER_ERROR. - A session-bound participant call that reads the current session first shares one deadline with that read, and
auth.session()honourstimeout. - An error status whose body breaks off, times out or is cancelled is now a
network,timeoutorabortedfailure that keeps itsstatusandretryAfter, in all three clients; an error body that is not JSON stayshttp. - Cancelling with
signalstops a retry wait at once. A write any attempt of which may have been applied staysuncertain, with itsoperationIdandrecovery, whatever later attempts answer. management.requestretries only GET, andmanagement.rawsends once.useInvitation()keeps a rate-limited code for a retry instead of discarding it as refused.
Avatar failures report
recovery: "reconcile"even when they have an upload ID. Wallet address and signature waits reject when the caller cancels. See Bounded requests and avatar recovery for the recovery-handler change.Generic management writes report
recovery: "reconcile"even with a caller-supplied action ID. Uncertaindomino apiwrites exposedetails.recovery: "reconcile". See Generic management writes require reconciliation for the recovery-handler change. - Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
1.0.0-next.20
No changes in this release.
1.0.0-next.19
Patch Changes
- 1141af4: Fix invalid action IDs in the tracker and leaderboard documentation examples. Validate action ID literals during documentation checks so copied examples use the identifier format the API accepts.
1.0.0-next.18
Minor Changes
-
8465d40: Operators can correct a member's tiered reward, at the revision they saw and with an audited reason.
management.handover.cancel(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/cancel,handoverpermission) returns a handover in progress toavailableand puts its pickup size back into stock.management.entitlements.correctCollection(…/correct,review) does the same for a collection recorded by mistake, andmanagement.entitlements.void(…/void,review) voids an available entitlement. A stale revision fails with 409entitlement-changed, the wrong starting status with 409entitlement-status. API keys with the permission can make these corrections in live. Console Rewards and member detail offer them, and Audit history lists each one with its reason.domino capabilities --jsonlists them asentitlement-corrections.Breaking:
Entitlementgains the status"void", withvoidedAt. A voided entitlement cannot be collected; a quest that grants the same reward to the member again makes itavailable, as a fresh grant at that quest's tier; a voided referral reward stays void. SDKs from before this release fail the whole progress read once a member has a voided entitlement. Reads now parse entitlements with a tolerantentitlementSchema(fields a newer server adds are kept, and an unknown status reads as"unsupported"withreportedStatus); the strict stored shape isentitlementStateSchema, and the management client'sentitlementRecordSchemais removed in favor ofentitlementSchema. To upgrade, see Entitlements can bevoid, and reads tolerate statuses they do not know.
1.0.0-next.17
Major Changes
-
c919948: Follow-ups to the React hooks' shared query layer.
completionCountLabel(view)is renamedcompletionCountText(view), so the name says it returns English likequestStatusText. For your own words, readview.completionCount.questStatus(view)returnsQuestStatus, whosecodeis the openQuestStatusCode, androleDeliveryStatus(deliveries, quest)returns the openRoleDeliveryStatus | null. Aswitchthat lists every code needs adefault, so a newer SDK can add codes without breaking the signature.useReferralSummary({ intervalMs })reads the summary again every 60 seconds by default while the page is visible, so friends who qualify show up without an app-side timer.useSignOut()drops the signed-out member's cached values as soon as Relay confirms the sign-out, even when the next session read fails.- A
useMyProfile()change that answers after another member signed in no longer writes into the new member's cache. useSignIn,useSignOut,useSignInOptions,useInvitationanduseQuestreturn objects and actions that keep their identity until something in them changes;useQuest's actions act on the latest view.useExperience().deliveriesis areadonly RoleDelivery[], and keeps one frozen empty list while there are none.progressOf(quests)is documented:totalcounts locked and paused quests and leaves out archived and repeatable ones.
Upgrade: React hooks share one cache and return one state shape in the SDK v1 upgrade guide.
1.0.0-next.16
Major Changes
-
bdb89a4: Evaluator readers follow one rule: a missing subject is
null, a failure throwsCapabilityError, linked accounts are pinned once per attempt, and tracker reads are point in time. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.- Missing is
null.ctx.x.profile(),follows()andreposted()returnnullwithout a linked X account instead of throwing;ctx.x.post()returnsnullfor a post that does not exist and needs no linked account.ctx.discord.member()returnsnullwithout a linked Discord account.ctx.wallet.list()returnsnullinstead of[], andctx.wallet.balance()returnsnullinstead oftotal: "0"when the member has no wallet of that family.ctx.trackers.content()returnsnullfor a missing record or observation. See Readers returnnullwhen an account is not linked or a subject is missing. - Failures throw
CapabilityError, new in@domino-sdk/relay/authoring(its wire shape,capabilityFailureSchema, is in@domino-sdk/relay/schema), withcode("not-linked","provider-unavailable","author-error") andretryable. Invalid capability input isauthor-error; an image check of{ x: "profile" }without a linked X account isnot-linked. Failures that are not retryable are captured and replay. A retryable failure that quest code handles and moves past is final for the attempt; one that escapes the evaluation is released, along with every fact captured after it, and retried, so the retry asks the provider and the later calls again. Rethrow a caught failure, or wrap it with{ cause: error }, to let it be retried. The failure shape is open: a newer Relay can add codes, and the SDK treats an unknown code as not retryable. See Capability failures throwCapabilityError. - Tracker reads are point in time.
ctx.trackers.records()andsummary()takeasOf, defaulting toctx.eligibleAt, and every tracker read of an attempt sees the observations stored at its first tracker read. PassasOf: ctx.nowto count everything stored by then. See Tracker reads are point in time and pinned per attempt. - Linked accounts stay pinned. X and Discord readers pin the account at the attempt's first read and never recheck it, as wallets already did, so a link change no longer fails the attempt with "X account changed" or "Account or integration changed". See Linked X and Discord accounts stay pinned for the attempt.
Who should act. Evaluators that read
ctx.x,ctx.discord,ctx.walletorctx.trackers, or that match capability error messages. The changed return types point TypeScript at each read to update. Thediscord-membership,token-holderandcommunity-screenshotsnippets and the Discord starter'sjoin-the-serverquest are updated. - Missing is
1.0.0-next.15
Major Changes
-
e1cba88: Observations are removed, and every automatic activity arrives as a tracker record.
automatic({ source, type }),ctx.observation,management.observations.submit,POST /management/v1/observations, theObservationtype,observationSchema,observationInputSchema,observationReceiptSchema,observationTriggerSchema,discordReactionSchema,QuestView.trigger.observation(and the release trigger'sobservation) and theobservationfield of theautomaticavailability are gone.questStatusandrunsSilentlyfrom@domino-sdk/relay/reactnow label every tracker-triggered quest "Automatic", not only observation and keyed quests. A server that reported observations sends records to a custom tracker withmanagement.trackers.ingestand triggers quests withautomatic({ tracker }). Ingestion never joins a member to a project: a tracker trigger skips a member who has not joined withnot-joined. Quests published with an observation trigger leave the active set when Relay upgrades. Republish them from code on a tracker; the quest key can stay the same.Discord messages and reactions are two managed tracker sources,
discord.messages()(with{ text: true }to store message text) anddiscord.reactions(), on thediscordobject from@domino-sdk/relay/authoring. Domino's Discord gateway writes each new message or reaction into every tracker of that kind in the projects connected to the server, and keeps a record only when one of its accounts is a Discord account a member has linked. Their record data is typed (DiscordMessageData,DiscordMessageTextData,DiscordReactionData), sowherefilters andctx.tracker.record.dataare checked. A reaction record names the message's author as themessageAuthoraccount:automatic({ tracker: reactions, beneficiary: "messageAuthor" })replacesreaction.received, and Relay skips reactions to one's own message withbeneficiary-is-author. Discord trackers refuse ingestion (403) and refresh (422). Streaks work on these triggers.The
observepermission is removed. Ingesting needsingest, and enrolling a member from a server (management.content.enroll) needs the newenrollpermission. Editors holdread,publish,inventory,points,ingestandenroll; hosted-app keys holdread,pointsandingestand cannot enroll. A personal API key issued withobservekeeps its other permissions, andobservegrants nothing: reissue it with Ingest or Enroll. Theobservation-questscapability is removed fromcapabilities.json; Discord tracker triggers are part of thetrackerscapability.To upgrade, see Observation triggers become tracker triggers and The
observepermission is replaced byenroll.
1.0.0-next.14
Major Changes
-
c2390b3: The React hooks in
@domino-sdk/relay/reactshare one query cache and return one state shape.- Every read hook returns
{ status, data, error, refresh }:statusisloading,readyorerror, plussigned-outfor hooks that need a member;dataholds the value;erroris always aRelayError, and whilereadyit says why the latest refresh failed and the last value stays.useMeanduseSignInMethodsreturn this shape instead of a bare value,useMeno longer swallows errors, and the profile hooks report the uniform 404 aserrorwith status 404 instead ofmissing. <RelayProvider>keeps one keyed cache. Components that read the same thing share one request. A read that is replaced, invalidated, or belongs to a previous participant or client is cancelled, and its late answer is dropped. Polling, including the quest experience observer and the Discord role-delivery watcher, pauses while the tab is hidden and catches up when it is shown. Reads wait for the first session read, public ones included, and a sign-in attempt that does not change the session drops nothing.- Changes invalidate what they affect: every new completion the experience shows, submitted or automatic, reads balances, point history, leaderboards, profiles and referral progress again; an accepted invitation, the referral summary and referral progress; wallet, account and profile changes, their own reads. The
PROFILE_CHANGEDwindow event is removed.useInvalidate()andrelayKeysinvalidate by key prefix after a change made withuseRelay(). - The cache is scoped to the client, organization, project, environment, member and session. Signing in or out or switching accounts, projects or environments drops it, so one member never sees another's cached values. A new
clientorbaseUrlpassed toRelayProvidernow takes effect: the old observer stops, reads in flight are cancelled, and the hooks start from an empty cache. useSession()is new: who is signed in, independent of the experience read, so a failing/me/experienceno longer blanks the profile, wallet, account anduseMehooks.useExperience()returns the shared shape withdata.questsanddata.collectionsanddeliveries; itsmembermoved touseSession().Experienceis replaced byExperienceData, whichquestGroupstakes.useBalances()returns the{ items }answer indata, andusePointHistory(ledger)returnsdata.itemswithloadMore(); an unknown ledger or a member who has not joined iserrorwith codepoint-ledger-not-foundornot-joinedinstead of themissingandnot-joinedstatuses.BalancesStateandPointHistoryStateare replaced byMemberQueryandPagedQuery.useRelayQuery,useMemberQuery,useRelayPagesanduseMemberPagesput an app's own reads on the same cache.Pageis the root package's type;PagedQuery(public pages) andMemberPagedQuery(member pages) addloadMore,loadingMoreandloadMoreError, which belong to one key and participant.- Every error the hooks report is a
RelayError:useQuest().error,useSignIn().error,useSignInOptions().errorandmethodsError,SignInMethods.error(also fromsignInOptions()in@domino-sdk/relay/browser), and a rejected or failed invitation'serror, which replacesreason. completionMark,hasPending,sanitizeDeliveries,watchRoleDeliveries,PENDING_POLL_MS,RETRY_DELAYS_MS,runsSilentlyandvisibleQuestsare no longer exported from@domino-sdk/relay/react.questStatus(view)returns{ tone, code, reason? }, a stable code to localize (QuestStatusCode, an open string likeRelayErrorCode);questStatusText(view)adds the Englishlabelanddetail.roleDeliveryStatus(deliveries, quest)returnspending,failed,grantedor null, androleDeliveryLabel(status)gives its English label.
Upgrade: React hooks share one cache and return one state shape in the SDK v1 upgrade guide maps every hook.
- Every read hook returns
1.0.0-next.13
Major Changes
-
23c85ba:
defineEngagementRewardtakesbudgets, a list of{ scope: "project" | "member", period: "day" | "week" | "month" | "lifetime", limit }, instead ofbudget: { projectDaily, memberDaily, projectLifetime }. Periods are those of the project calendar at settlement time, so a rule can now cap weekly or monthly payouts, and accounts settle once per project calendar day instead of once per UTC day. A definition withbudgetfails validation; rules already published keep their limits in the new form.See Engagement budgets are a list of periods in the v1 upgrade guide.
-
23c85ba: Each project environment has one project calendar: a timezone, UTC until you choose one, that sets where every day, week and month begins. Streak days, quest budgets, engagement budgets and calendar leaderboard windows all follow it, so a weekly budget and a weekly leaderboard cover the same week.
- Set it with
"calendar": { "timeZone": "Europe/Budapest" }in relay.json, in Console Settings → General → Project calendar, or with the newmanagement.calendar.read()andmanagement.calendar.configure({ timeZone, reason })(GETandPOST /calendar,publishpermission; in live only from Console). A change takes effect at the next midnight in the current timezone: the open day, week or month finishes in the old zone, and streaks stay consecutive. A deployment applies relay.json's timezone only when it differs from the last one deployed, and the deployment preview's newcalendarfield shows the change it schedules. streak()and calendar leaderboard windows no longer take atimeZone; both throw if given one. Streaks and boards already published with one still read.- Budgets reset at midnight in the project timezone instead of UTC, and accept
period: "month". - One validator,
timeZoneSchema, accepts named IANA zones only. Fixed offsets such as+02:00andEtc/GMT-2are rejected, including in leaderboards, which used to accept them. The sharedcalendarPeriodSchemaislifetime | day | week | month.
Who should act. Projects that pass
timeZonetostreak()or a calendar leaderboard window, and projects that rely on budgets resetting at UTC midnight. See Set the timezone once, on the project calendar in the v1 upgrade guide. - Set it with
1.0.0-next.12
Minor Changes
-
710e520: Participants read their own point balances and history.
relay.me.balances()(GET /v1/me/points) returns{ items }: the signed-in member'sbalanceandavailablepoints (after reservations) in every ledger of the project, each with itsledgerreference.relay.me.pointHistory({ ledger, cursor? })(GET /v1/me/points/:ledger/history) pages through the member's own entries in one ledger, by key or ID, most recently recorded first, as{ items, nextCursor }. Each entry is{ id, amount, kind, at, quest, reverses }, without the operator's reason or actor.kindis an open string; thePointEntryKindconstants (award,spend,adjust,reverse) name the kinds this SDK knows, and referral payouts areadjust. An unknown ledger answers 404 with codepoint-ledger-not-foundand a member who has not joined the project 404 withnot-joined, both added toRelayErrorCode. Neither read takes a member, and both are scoped to the session's project and environment. React apps useuseBalances(), which reads again after a quest completes, andusePointHistory(ledger). The participant proxy and identity integrations admit both routes. New schemas and types:pointBalanceSchema,pointBalancesSchema,pointEntrySchema,pointHistoryQuerySchema,pointHistoryPageSchema,PointBalance,PointBalances,PointEntry,PointEntryKind,KnownPointEntryKind,PointHistoryQueryandPointHistoryPage.The classic starter's header now reads the balance with
useBalances()instead of its/api/me/pointsserver route, which is gone.
1.0.0-next.11
Minor Changes
-
f2f1b75: Breaking: the management client moves from
@domino-sdk/relay-clito the new@domino-sdk/relay/serverentrypoint, withcreateManagementClient,hostedManagementClient,ManagementError, the management schemas and types, andstarterTemplates. Server code, including a hosted app's Worker, now needs only@domino-sdk/relay, without the CLI's command-line parser and TypeScript compiler.@domino-sdk/relay-cliis only thedominoexecutable and its agent skills; it has nothing to import.serverBoundary()also fails a browser build that imports@domino-sdk/relay/server. To upgrade, see Import the management client from@domino-sdk/relay/server.The capability catalog (
domino capabilities --json) adds staff confirmation, staff handover, stock, point corrections, deploy and stage, operator member reads and lookup, project access, and the management API withdomino api. It marks webhooks, bulk data export, completion revocation and tracker-scoped keys as unsupported, and lists the management methods it leaves out on purpose inexcludedManagementMethods.
Patch Changes
- 909f343: The browser submission store recovers from storage failures instead of caching them. A temporary IndexedDB opening error, a blocked open, a
versionchangefrom another tab, or a connection the browser closes no longer fails every later call on the same client: the nextstatus(),resume()orsubmit()opens storage again, and the saved operation and its operation ID are kept. A blocked open waits briefly for the other tab, then rejects withsubmission-storage, and the pending request is adopted if it later succeeds. Full browser storage rejects with the newRelayErrorCode.SubmissionStorageQuota(submission-storage-quota); an upgrade by a newer page asks for a reload. Both areclienterrors that are notuncertain, and a failed read never looks like an empty store, so a new claim is never started over a saved one. See Storage failures in the participant API reference.
1.0.0-next.10
No changes in this release.
1.0.0-next.9
Minor Changes
-
cf5b02c: Custom tracker records have a declared data schema.
customSource(namespace, { data })requires a zod object schema (zis exported by@domino-sdk/relay/authoring), stores its JSON Schema form with the tracker, and throws, naming the field, for what ingestion cannot check (string formats and patterns, tuples, refinements, transforms, dates, bigints). Publication refuses a custom tracker without one. Ingestion rejects a batch whose record data does not match with 422 and{ error, code: "invalid-record-data", record, path, reason }, one too costly to validate withrecord-data-too-complex, and records for a tracker published without a schema withtracker-schema-required. Schemas are bounded (32 KB, 16 levels, 256 properties, required keys, enum values or key names per node, 16 branches per union, no wide union nested in another union's branch), and validation charges every unit of work to a per-record and per-batch step budget. A deployment whose new schema breaks a live quest'swherefilter, Console quests included, is refused. Stored records are not rechecked: tracker triggers skip non-matching records with the new skip codeinvalid-record-data, evaluators reject pinned records that do not match the schema the quest was built with, andtrackers.records(handle)leaves them out and counts them inexcluded. The schema can change under the same tracker key and applies to later records.The data type flows into quests:
ctx.trackeris non-null in a tracker-triggered quest's evaluator withrecord.dataof the tracker's type (andctx.contentandctx.observationare non-null for the triggers that supply them,nullotherwise),trackers.records(tracker)returns typed records, andwherepaths,equalsvalues and{ setting }bounds are type-checked against the data and the quest's integer settings.wherealso accepts a list of conditions that must all hold, andautomatic()and publication check filter paths against the stored schema.settings.objectanddefineQuestaccept onlysettings.*builders; a plain zod setting is a type error anddefineQuestthrowsSetting "<key>" is not a quest setting.See Custom sources declare their record data and Quest settings come only from settings builders in the upgrade guide.
1.0.0-next.8
Major Changes
-
54fcf41: The participant client follows one convention for paging, sessions and errors.
- Every method takes an optional last argument,
{ session?, signal? }.sessionreplaces the positional session argument ofwallets.challenge,wallets.verifyandme.collectionPass. Calls Relay binds to a session (wallet linking, collection passes,quests.retry) read the current session whensessionis omitted. - Every paged read takes
{ cursor? }and returns{ items, nextCursor }:referrals.history,content.admissions,content.qualifications,content.performance,engagement.pageandengagement.boundarieschanged frombefore,afterandnext. The participant routes answernextCursorand readcursor, and so do the management routes and client methods that share them (management.referrals.history(member, { cursor }),management.content.*,management.engagement.pageandboundaries). - Every rejection is a
RelayError, which extends the new sharedRequestError:kind(http,network,timeout,aborted,invalid-response,client),uncertain(true only for a write that may have taken effect: a failure after sending, a cut-off body, a 500, 502 or 504, or an unreadable success answer),statuswhenever response headers arrived, an open stringcode(known ones inRelayErrorCode),operationId, andrecovery(replaywith anoperationId, elsereconcile) when uncertain.isRelayErrorandisRequestErrorrecognize these errors across duplicate copies of the package.RelayError.statusis nownumber | undefined. Invalid input rejects withinvalid-inputinstead of aZodError, a success answer this SDK cannot read withinvalid-response, a body cut off after its headers with kindnetworkand its status, and a wallet signer's own failure withwallet-signer. The error body is read field by field, so Relay's message and a code the SDK does not know are always kept. Relay now sendscode: "not-signed-in"andcode: "stale-session"on those participant errors, and every paged read answers a malformed cursor with 400, codeinvalid-cursor, and one message. - Removed:
submit,retry(id),progress(member),upload(member, file),auth.connections,me.linkPass, thewallet.*namespace, and the staff calls the participant API refused (lookupMember,snapshot,publish,publishBatch,beginHandover,confirmHandover,decide,journeyTemplate,template). - Parameters take Zod input types, so defaulted fields are optional, and cursors are opaque strings the server validates. The session the client returns (
ParticipantSession) accepts fields and principal kinds a newer Relay adds;sessionMember(session)reads its member. - Hosted proxies no longer forward the 0.x single-wallet routes (
/v1/me/wallet,/challenge,/verify).
Upgrade: One paging, session and error convention in the participant client in the SDK v1 upgrade guide maps every method and has search commands.
- Every method takes an optional last argument,
1.0.0-next.7
Major Changes
-
1084288:
@domino-sdk/relaynow exports only the participant runtime client:createRelayClient,RelayError,QuestUpdatedError,formatUnits,parseUnits, and the types the client returns. Schemas, builders and avatar helpers moved to their own entry points, a few implementation helpers are no longer public, and two are no longer exported at all, so an app that imports the client no longer loads authoring builders, deployment and management schemas, or DiceBear.- Zod schemas (and helpers such as
identifier,caip10andxPostId) come from@domino-sdk/relay/schema. - Builders (
defineTracker,defineLeaderboard,defineReferral,defineEngagementReward,content,perContent,streak,settings,discord,pointLedgers,x,customSource) and evaluator reader types (TrackerReader,XReader,ImageReader) come only from@domino-sdk/relay/authoring. avatarSrc,defaultAvatar,defaultAvatarSvgandwalletAvatarSeedcome from@domino-sdk/relay/avatar, and no longer from@domino-sdk/relayor@domino-sdk/relay/react.- No longer public:
identityIntegrationSchema,identityIntegrationInputSchema,identityIntegrationSecretSchema,participantExchangeSchema,questConfiguration,parseImageResult,parseImageExtraction,isContentParticipantRoute,isProfileParticipantRoute,isWalletParticipantRouteandisWalletSignInRouteare exported only from@domino-sdk/relay/internal, which Relay's own services share and which is not a supported API. - No longer exported:
questActionsandreconcileQuestDraft. Open a quest controller withrelay.quests.open(view); itsquest-updatedresult already carries the reconcileddraft. - The package declares
sideEffects: false. esbuildis an optional peer dependency, needed only by@domino-sdk/relay/build.@domino-sdk/relay-clidepends on it directly, sodomino buildanddomino deploywork as before. Code that callsbuildQuestwithout the CLI must installesbuild.
Upgrade: Runtime entry exports only the client in the SDK v1 upgrade guide has the import mapping and search commands.
- Zod schemas (and helpers such as
1.0.0-next.6
Patch Changes
- 8b706af: An attempt now records when its evaluator asked for human review on a quest with review off.
decision.review({ reason })was already a rejection with the review reason in that case. The attempt now also hasrequestedReview: true, and Console attempt detail says "Would have gone to review; human review is off". A reject the evaluator returned itself keepsrequestedReview: false. Thereviewoption, the quests concept page and the authoring reference describe it.
1.0.0-next.5
No changes in this release.
1.0.0-next.4
Minor Changes
- d7f9fe4:
streak(...)now completes manual quests and tracker-triggered quests (automatic({ tracker })), not only automatic observation quests. Each accepted attempt counts the community day of its trusted occurrence time, the timectx.eligibleAtreports: a tracker record observation'soccurredAt(such as an X post's publication time), or, for a manual quest, when Relay admitted the submission, so a manual streak counts submission days.defineQuestno longer throws "Streaks require an automatic observation trigger" or "Tracker triggers complete once() or keyed()", and publication accepts these quests. Content quests and prerequisite-onlyautomatic()quests still cannot be streaks, and a streak quest keeps its trigger kind (manual, observation or tracker): changing it needs a new quest key. Once a member's tracker streak milestone pays, the trigger stops admitting that member's observations for the quest, as for a completedonce()quest.questStatusshows a member's own streak submission first while it is checking, in review or failed; a rejected submission keeps the "N / M days" progress and gives the reason as its detail. The newpackages/sdk/examples/posting-streak-quest.tspays members who post on X five days in a row. Nothing existing needs to change, so the v1 upgrade guide has no section for this.
1.0.0-next.3
Major Changes
-
a803b30: Evaluators now replay deterministically. Hosted evaluation freezes the clock for each attempt and seeds randomness from the attempt, so a retried or replayed attempt makes the same decision and
keyed()completion key. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.ctx.nowis new: the attempt's evaluation time in epoch milliseconds, the same on every run of the attempt.keyed()receives it too. It is distinct fromctx.eligibleAt, which is when the activity happened.Date.now(),new Date()andDate()returnctx.nowand do not advance, even across awaited capability calls.performance.now()returns 0.Math.random(),crypto.getRandomValues()andcrypto.randomUUID()repeat the same sequence for one attempt and differ between attempts.
Who should act. Evaluators that measure elapsed time or rely on unpredictable random values. Use
ctx.nowfor "as of evaluation" checks andctx.eligibleAtfor when the activity happened. See Evaluators see a frozen clock and seeded randomness in the v1 upgrade guide.
Patch Changes
- 7e37049: The SDK's optional React peer dependency is now
^19.0.0instead of>=19, matching the React versions the React hooks are verified against. React is still optional: the runtime, browser and server entrypoints do not need it. Apps on React 19 need no change.
1.0.0-next.2
Minor Changes
-
c320586: A content quest's
performance.evaluatecan no longer select rewards. It gates the rewards the content evaluation already selected, and it always did: arewardsvalue in its result was silently ignored. Its type is nowPerformanceOutcome(accept,revieworrejectwith a reason only), sodecision.accept({ rewards })anddecision.review({ reason, rewards })are type errors there, and a runtime result that carriesrewardsfails the evaluation with "A performance evaluator cannot select rewards". To change what a post earns, select rewards in the quest'sevaluate.See Performance evaluators no longer select rewards in the upgrade guide.
-
c320586: A prerequisite is the quest key alone.
completed(quest)returned{ quest, scope: "ever" }, andscopewas declared but never read: a prerequisite has always meant a required quest completion from any prior occurrence, with no same-period variant.completed(), the quest description schema (requires[]) and published releases no longer carryscope. Releases stored with it still read, and the field is dropped. Code that readsrequires[].scope, or builds{ quest, scope: "ever" }by hand, should use{ quest }orcompleted(quest).See Prerequisites are typed eligibility rules in the upgrade guide.
Patch Changes
- c320586:
defineQuestthrows at build time for two shapes that only failed when publishing: a bareautomatic()quest with norequiresentry, and a bareautomatic()quest withkeyed()completion. A bareautomatic()runs once its prerequisites are complete, so it needs at least onecompleted(quest)and completes once. The backend still checks both when it publishes. - c320586:
manual({ actor: "staff", input })throws instead of dropping the actor. Its types never allowed the combination, but untyped callers got a quest that members could submit evidence to, not one staff confirm. Staff-captured evidence is not supported: usemanual({ actor: "staff" })for a staff confirmation, ormanual({ input })for evidence the participant submits.
1.0.0-next.1
No changes in this release.
1.0.0-next.0
Major Changes
-
5d4ec3e: Start the SDK v1 prerelease. Relay SDK and CLI v1 is one breaking release, 1.0.0; until it ships, breaking changes publish as
1.0.0-next.Non the npmnextdist-tag.lateststays on the 0.x release, sonpm install @domino-sdk/relayand new starters keep using 0.x.Who should upgrade. Install
@domino-sdk/relay@nextand@domino-sdk/relay-cli@nexttogether only to try v1 before it is stable. Prereleases can break each other: pin an exact1.0.0-next.Nversion withnpm install --save-exact @domino-sdk/relay@next @domino-sdk/relay-cli@next(orpnpm add -E @domino-sdk/relay@next @domino-sdk/relay-cli@next), and upgrade the SDK and CLI to the same version.Upgrade steps. Every breaking change in the prerelease adds a section to the SDK v1 upgrade guide in the Domino docs, and its changelog entry links to that section. Once 1.0.0 is published, stable APIs follow semantic versioning; experimental APIs are only in namespaces marked experimental.
0.31.1
No changes in this release.
0.31.0
Minor Changes
-
3d0bee0: Tracker triggers are composable primitives: a firing mode, an optional data filter, and the account role a quest pays, over records whose accounts can be any verified identity.
automatic({ tracker, on?, where?, beneficiary? }).onis"new-record"(default for X trackers),"new-observation"(default for custom trackers) or"data-changed"(a new record, ordatathat differs from the previous observation).whereis one condition on a path in the record'sdata:{ path, equals }, or{ path, gt?, gte?, lt?, lte? }comparing a number or an integer string exactly, with bounds that are numbers or{ setting: "name" }(an integer setting of the quest). Observations the mode or filter does not select record nothing;ingestcounts those the filter dropped inadmissions.filtered, present when nonzero.beneficiaryis an account role:"author"(default),"inReplyTo"on X trackers, or a role a custom source declares. A record never pays its author's member through another role: when the beneficiary account is the author's, or both belong to the same member (the same wallet on another chain, or another account the member linked), the observation is skipped withbeneficiary-is-author.customSource(namespace, { identities, accounts? }): record authors and named accounts are verified identities of the listed kinds,"wallet","x","discord"or"member". Records carryauthor: { id, kind? }andaccounts: { <role>: { kind?, id } };kindmay be left out when the source lists one kind. Record reads return them as ingested, and the recordsmemberfilter matches authors of every declared kind. The ingesting server names the accounts, so keep ingest keys on trusted servers.- Trigger outcomes carry a stable skip
code(trackerSkipCodes) beside the humanreason, andtrackers.outcomesfilters bycode. An X post without an author is no longer reported as "Post is not a reply". triggerMode(policy),beneficiaryRole(policy)andsourceIdentities(source)read the mode, role and identity kinds of published policies and trackers, including those stored by earlier SDKs.
Compatibility. Existing data keeps its stored shape on the wire, so SDKs and CLIs 0.25 to 0.27, and backend pods of the previous release during a rollout, keep reading it. New keys appear only where an author uses a new option:
automatic()leavesonout when it is the source's default, so default quests publish the same policy as before.on(non-default),where, roles other thanauthorandinReplyTo,identitieson new trackers,accounts,author.kind, anddiscordormemberattribution are rejected by older SDKs, whose schemas are strict: upgrade apps that read those trackers and quests.- Trackers published with
authors: "wallet"read back as stored. Republishing one asidentities: ["wallet"]is the same tracker: it keeps its key and stored definition. - Reply quests published with
beneficiary: "replied-to"keep it: reads return it as stored, and their evaluators keep receivingbeneficiary: "replied-to", so they keep working until republished. Republishing with this SDK switches them toinReplyTo. - Trigger outcomes always carry
code:trackers.outcomesneeds this SDK.
Breaking changes. For code written against the tracker triggers of 0.25.0 to 0.27.0:
- Replace
customSource(namespace, { authors: "wallet" })withcustomSource(namespace, { identities: ["wallet"] }).authorsstays readable on published trackers and is deprecated. - Replace
automatic({ tracker, beneficiary: "replied-to" })withbeneficiary: "inReplyTo". TrackerBeneficiaryandtrackerBeneficiarySchemaare removed; the beneficiary is a role string, or"replied-to"on releases published before roles.
Upgrading. Deploy the backend (migration
0074_tracker_trigger_primitives) before publishing quests that use the new options; older servers refuse them.
0.30.0
Minor Changes
-
c6afc57: The EVM chains
ctx.wallet.balance()reads are the platform's list, not an SDK export. Adding a chain no longer needs an SDK release.WalletBalanceQuery.chainfor EVM is any CAIP-2 ID, typed`eip155:${number}`(newEvmChainIdtype). NewisEvmChainId(value)(also from@domino-sdk/relay/authoring) andevmChainIdSchemacheck the syntax (eip155:and a chain ID in decimal, no leading zeros); the evaluator sandbox refuses anything else before reading.- The platform decides which chains it reads: the EVM networks
management.walletBalanceChains()lists, generated from dRPC's chain list. On any other EVM chain the evaluation fails with 422 "Relay cannot read balances on eip155:: it is not a supported EVM chain. …". - New
management.walletBalanceChains()(GET /management/v1/wallet-balance-chains,readpermission) returns{ chains: [{ chain, name, testnet }] }: Solana mainnet, then each EVM chain the platform reads, by chain ID. Console names the chains of a member's balance checks from it.
Breaking.
evmChains,evmChainSchema,EvmChainandEvmChainInfoare no longer exported from@domino-sdk/relay.Upgrading. Quest code that passes a string literal such as
"eip155:8453"compiles unchanged. ReplaceEvmChainwithEvmChainId,evmChainSchemawithevmChainIdSchema(it checks syntax, not support), and chain-name lookups inevmChainswithmanagement.walletBalanceChains()from your server. -
c6afc57:
ingestis a normal management permission: it sends records to custom trackers (management.trackers.ingest) and allows nothing else.- Editors hold
ingest, and organization administrators hold every permission, somanagement.access()lists it for them.rolePermissions.editorincludes it. - Personal API keys can carry it. In Console, Settings → Keys → Personal API keys now lets you choose a new key's permissions (all of yours by default) and lists each key's permissions; select only Ingest for an indexer.
POST /management/v1/tokensacceptsingestfor callers who hold it. - Hosted-app keys (
dhk_) now listingestin the minted key, the development key andaccess(), alongsidereadandpoints. They always held it. observestill allows ingestion, so keys issued with onlyobservekeep ingesting. A caller with neither gets 403 "Missing ingest permission" (before: "Missing observe permission").domino devneeds yourread,pointsandingestpermissions for its development key; editors have all three.tokenSchema(the personal API key list) drops permissions this release does not know instead of rejecting the list, asaccess()already does.
Upgrading. The
dominoCLI reads/accessand its development key without checking permission names, so every CLI version keeps working. Code that callsmanagement.access()from@domino-sdk/relay-clibefore 0.25 rejects a response that listsingest, which now happens for every editor and administrator: upgrade@domino-sdk/relay-clithere. 0.25 and later drop permissions they do not know. - Editors hold
0.29.0
Minor Changes
-
28f8bcf: Participant profiles are primitives: Domino serves the profile and separate reads keyed by it, and your app composes the profile page. Each read checks the profile's visibility and then its own resource's access.
relay.profiles.get(id)returns the profile only:id,nameandavatar(profileSummarySchema).relay.leaderboards.placement(board, profileId)(GET /v1/leaderboards/:board/profiles/:profile) returnsrank,scoreand the rankedtotalon one board, withrankandscorenull while the member is unranked. It needs the board's access, likestandings. NewleaderboardPlacementSchema.relay.profiles.completions(id, { limit, cursor })(GET /v1/profiles/:profile/completions) returns completions grouped by quest, most recent first, paged withnextCursor. Only quests that opt in appear:defineQuest({ profile: { visible: true } }), hidden by default; the quest's active release decides, and archived quests do not appear. NewprofileCompletionPageSchema,profileCompletionQuerySchemaandquestProfileSchema; quest descriptions gain an optionalprofile, whichdefineQuestincludes only when the quest sets it.relay.profiles.referral(id, program)(GET /v1/profiles/:profile/referrals/:program) returnsinvited,qualifiedandmilestonesin one program, for signed-in viewers (401 when signed out). NewprofileReferralSchema.- React:
useLeaderboardPlacement(board, id),useProfileCompletions(id, { limit })withloadMore(), anduseProfileReferral(id, program).useProfile(id)returns the profile only. - Profile settings:
defaultsis an ordered list of the accounts that fill in names and pictures,"x"and"discord"([]for generated).profileDefaultProviderSchemalists them. - Generated avatars are seeded by an HMAC of the member under the project's secret (
relay.me.get().avatarSeed), no longer by the member's first wallet: linking a wallet never changes an avatar. proxyParticipantand the API's scope selector admit the new routes (isProfileParticipantRoute).- Console: Participant profiles settings show who sees profiles and the default accounts, with arrow buttons to order them. The member's Community profile appears only when profiles are visible or the member or an operator changed the profile.
Upgrading. Breaking for apps, servers and CLIs on 0.25 to 0.27 that use profiles:
relay.profiles.get(id)no longer returnsself,leaderboards,questsorreferrals, andpublicProfileSchemaandPublicProfileare removed. Read each part with the calls above. Forself, compareidwithrelay.profiles.mine().id.useProfile(id)'sreadystate holds aProfileSummary.- Quest completions no longer appear anywhere until the quest opts in: add
profile: { visible: true }to the quests whose completions profiles may show, and publish. - Referral progress is per program and needs a signed-in viewer.
attributedis nowinvited, returned with each program; it still counts the member's invitations across the whole project, not only that program's. management.profiles.configure()takesdefaults: ["x", "discord"]instead of"accounts", and[]instead of"generated";sectionsis removed and refused.settings()returns the list. Stored settings migrate:accountsbecomes["x", "discord"],generatedbecomes[].- Members who linked a wallet get a new generated avatar, the one members without a wallet already had. Uploaded and account pictures do not change.
- SDK and CLI 0.25 to 0.27 cannot parse the new profile and settings responses:
relay.profiles.get()andmanagement.profiles.settings()throw until the app and CLI upgrade. Deploy the backend first; its migration only adds the new setting, so pods of the previous release keep working during the rollout. - Upgrade the CLI and finish rolling out the backend before publishing a quest with
profile: { visible: true }. Only quests that setprofilecarry the key in their release. CLIs from 0.25 to 0.27 refuse deploy and preview responses that include such a release (Unrecognized key: profile), and pods of the previous backend release cannot read it, including after a rollback. Quests without it publish and roll back as before.
0.28.0
Minor Changes
-
30dda1d: Add
useSignInOptions({ cosmos })to@domino-sdk/relay/react: every way a participant can sign in, as one flat list. It holds the configured OAuth providers, the installed wallets of the families the project enabled, and email. Each option has anid, alabel, akindand aniconhint. OAuth and wallet options sign in withstart(), and the email option withsendCode(email)thenverifyCode(code). Cosmos wallets are offered only on the chains the app passes ascosmos.unavailableexplains the gaps with a machine-readablereason:cosmos-chains-missingwhen the app named no chains,cosmos-chains-invalidwhen a chain incosmosis invalid (such as"cosmoshub-4"withoutcosmos:; the other methods still load), andno-wallet-installedwith a message for the participant. OAuth and email are offered as soon as the methods are read; installed wallets join them when discovery finishes. The options keep their identity across renders, and their actions never reject: failures land inerror.methodsErrorsays when the methods could not be read and the list fell back to email. Apps without React get the same list fromsignInOptions(relay, { cosmos })in@domino-sdk/relay/browser, whose actions reject on failure;@domino-sdk/relay/browseralso exports the option types,discoverSignInWallets,CosmosChainsandSignInWallet.useSignIn()actions now keep their identity across renders, andverifyCodealways uses the latest code sent;sendCodeforgets the previous code first.useSignInMethods()returnserrorwhen it fell back to email. Installed wallets with the same name and family get a number in their label.cosmosChain()now rejects a chain object without a stringchainId.The starter apps now build their sign-in on the hook, and no longer export
COSMOS_CHAINS. To offer Cosmos wallets in an app made from a starter, pass your chains to theuseSignInOptions()call insrc/components/sign-in.tsx. In development the starters show a note when the project takes Cosmos wallets and the app names no chains, or an invalid one, and no longer tell participants the project has no sign-in method in that case.useSignInMethods(),useSignIn()anduseSignInWallets()keep working.Management member accounts now carry
kind:walletfor wallet identities andaccountfor the rest. Responses from older servers read asaccount.
0.27.0
Minor Changes
-
9ef2629: Cosmos wallets work on any Cosmos SDK chain your app chooses. Relay no longer ships a list of Cosmos chains.
- A Cosmos wallet is
cosmos:<chain ID>(a CAIP-2 reference: 1 to 32 letters, digits,-or_) and a bech32 address with any prefix, checked for its checksum and stored lowercase.caip10()and wallet tracker authors accept any Cosmos chain ID. A Cosmos wallet is its address: a Cosmos account ID is attributed to the member who links that address, andtrackers.records({ member })returns the member's Cosmos records under any chain ID. The same key on chains with different prefixes is separate wallets; five Cosmos wallets per member and project, across all chains. 32-byte module and contract addresses cannot link or sign in. Unlinking a wallet and changing its sign-in accept a bech32 address in either case. - A Cosmos wallet's
networkis the chain the app requested when linking it. Relay does not verify it: any app can link an address under anycosmos:<chain ID>. Quests and servers should gate on the address, such as its bech32 prefix against their own list of chains, not onnetwork. - Browser:
cosmosSigner(provider, chain)anddiscoverCosmosWallets(chain)take the app's chain as{ chainId, chainInfo? }or"cosmos:<chain ID>". When the wallet answers that it does not know the chain, the signer suggests it withexperimentalSuggestChain(chainInfo)and retries. WithoutchainInfo, or with a wallet that has noexperimentalSuggestChain, it throws an error naming the chain. Other errors, such as a rejected request, pass through. NewCosmosChaintype andcosmosChain()helper. - React:
useWalletDiscovery({ cosmos })anduseSignInWallets(families, { cosmos })take the app's chains (one or an array). - Wallet sign-in is enabled per family:
"solana","eip155"and"cosmos".relay.auth.providers().wallets,participantSignIn()andconfigureParticipantSignIn()use families, and a per-network value such ascosmos:cosmoshub-4is accepted and read ascosmos. The rule that refused a Cosmos testnet in the live environment is gone with the chain list. NewwalletSignInFamiliesandwalletSignInNetworksSchema. - Console: Participant sign-in offers Cosmos SDK chains, and member wallets show the chain ID, such as Cosmos · cosmoshub-4. Starters offer Cosmos wallets for the chains in
COSMOS_CHAINSinsrc/components/sign-in.tsx, and say sign-in is not set up when the project enables only Cosmos wallets andCOSMOS_CHAINSis empty.
Upgrading. Deploy the backend, then the API Worker right after it. The backend's migration turns stored per-network sign-in settings into
cosmos. Until the API Worker is updated, it checksctx.wallet.list()results against the previous release's two Cosmos networks, so a quest evaluation fails when the member has a wallet linked on any other Cosmos chain.-
Projects that enabled Cosmos sign-in must upgrade their app to this release immediately after the backend deploys. SDK 0.25 and 0.26 read
relay.auth.providers().walletsagainst a fixed list of values. After the migration such a project answers["cosmos"], which that list does not contain, sorelay.auth.providers()throws for every visitor.useSignInMethods()then falls back to email alone: every OAuth and wallet sign-in button disappears, not only Cosmos wallets, and the email form appears even where email sign-in is off. -
CLI and management SDK 0.25 and 0.26 reject the
cosmosfamily inparticipantSignIn()andconfigureParticipantSignIn()responses. SDK 0.25 and 0.26 reject a linked wallet on any other Cosmos chain inrelay.wallets.list()andmanagement.member(). Upgrade the CLI with the app. -
Apps made from a starter declare
NETWORK_NAMES: Record<WalletSignInNetwork, string>insrc/components/sign-in.tsxwith a key per Cosmos network. After upgrading,tscfails on those keys. Built anyway, the sign-in form of a project that enables Cosmos wallets lists no Cosmos wallet, and crashes when it shows the "Wallet sign-in accepts …" hint, becauseNETWORK_NAMES.cosmosis undefined. Replace the per-network keys with onecosmoskey, declare your chains, and pass them touseSignInWallets:import type { CosmosChain, WalletSignInNetwork } from "@domino-sdk/relay" /** The Cosmos SDK chains this app offers wallet sign-in on. */ export const COSMOS_CHAINS: CosmosChain[] = [{ chainId: "cosmoshub-4" }] const NETWORK_NAMES: Record<WalletSignInNetwork, string> = { solana: "Solana", eip155: "EVM", cosmos: "Cosmos", } // In the sign-in component: const wallets = useSignInWallets(methods?.wallets, { cosmos: COSMOS_CHAINS, }) -
cosmosNetworksis removed, andCosmosNetworkis anycosmos:<chain ID>. Pass your chain to the adapters instead of looking it up. -
useWalletDiscovery()no longer lists Cosmos wallets on a default chain: pass{ cosmos: { chainId: "…" } }.useSignInWallets(networks)lists Cosmos wallets only with{ cosmos }. -
WalletSignInNetworkis now"solana" | "eip155" | "cosmos"; aSignInWallet'snetworkis its family, and a Cosmos wallet's chain issigner.network.
- A Cosmos wallet is
0.26.0
No changes in this release.
0.25.1
No changes in this release.
0.25.0
Minor Changes
-
6634992: Participants have project profiles: a display name and an avatar that other participants see on leaderboards and profile pages, where the project enables them.
relay.profiles.mine(),update({ displayName, avatar, hidden }),uploadAvatar(file, { uploadId })andget(profileId). Uploads accept JPEG, PNG or WebP up to 5 MB and are stored as 512×512 WebP without metadata.- React:
useMyProfile()anduseProfile(id);avatarSrc(avatar)renders any profile avatar. Generated avatars use the same seed asrelay.me.get().avatarSeed. - Contract:
profileSummarySchema,ownProfileSchema,publicProfileSchema,profileUpdateSchema,displayNameSchema,profileSettingsSchema,profileModerationSchema,managedProfileSchemaandisProfileParticipantRoute(). Leaderboard rows gainprofile(null unless the viewer can see it; older servers omit it and it reads as null). - Management:
profiles.settings(),profiles.configure({ visibility, defaults, sections, expectedRevision, reason })(publish; in live only from Console) andprofiles.moderate(member, { resetName, removeAvatar, lock, reason })(review).member(id)returnsprofile, and account profiles gainusername. Console has Settings → Access → Participant profiles and a Community profile section on the member.
Upgrading. Nothing is shown until an operator enables profiles: leaderboards keep their aliases and profile pages answer 404. Deploy the backend first; the database migration runs automatically and dates earlier completions. Apps must upgrade the SDK so their
/relayproxy (proxyParticipantorcreateParticipantBackend) admits/v1/me/profile,/v1/me/profile/avatarand/v1/profiles/:id; older proxies answer 404 for them. X and Discord names and pictures fill in when members next sign in or connect those accounts. No breaking changes. -
531f41e: Members can link several Solana, EVM and Provenance wallets to a project and unlink them.
relay.wallets.list(),link(signer),unlink(wallet), and the lower-levelchallenge()andverify().- Dependency-free signer adapters and discovery in
@domino-sdk/relay/browser:evmSigner,solanaSigner,walletStandardSolanaSigner,cosmosSigner,discoverEvmWallets,discoverSolanaWallets,discoverCosmosWallets. - React:
useWallets()anduseWalletDiscovery(). - Evaluators:
ctx.wallet.list()returns the member's linked wallets, pinned once per attempt. - Management:
unlinkMemberWallet(member, { chain, address, reason })(requiresreview), andwalletsin the member detail. - Contract helpers:
caip10(),cosmosNetworks,isWalletParticipantRoute(), and theLinkedWallet,WalletSignerand related schemas.
Upgrading. Nothing breaks.
relay.wallet.connection()/challenge()/verify()andctx.wallet.connection()/solanaBalance()keep working and use the newest linked Solana wallet; linking throughrelay.wallet.verify()still replaces it, while wallets linked throughrelay.walletssit alongside older ones. The legacy challenge text now uses the sign-in message format, and rate-limited wallet challenges answer 429 instead of 400. Deploy the backend and API Worker before publishing apps that use the new APIs. Apps that proxy throughproxyParticipantforward/v1/me/wallets*after upgrading the SDK. The database migration runs automatically. -
c71712f: Quests can trigger from custom tracker records attributed to members' verified wallets.
customSource(namespace, { authors: "wallet" }): each record's author is a canonical CAIP-10 account ID, attributed to the member who links that wallet. Ingestion refuses missing or non-canonical authors.automatic({ tracker })admits one attempt per new record observation for the attributed member. Evaluators andkeyed()receive the pinned observation and attribution asctx.tracker;ctx.eligibleAtis the record's occurrence time.management.trackers.outcomes(tracker, query)explains each admission or skip with its reason;management.trackers.retryOutcome(tracker, input)(requiresreview) retries a skipped admission.management.trackers.records(tracker, { member })works on wallet trackers, returning the member's linked wallets' records.trackers.ingestresults includeadmissionscounts.- New
ingestpermission, granted to hosted-app keys: it allows custom tracker ingestion only. People keep ingesting withobserve.
Upgrading. Nothing breaks for existing projects. Once a project publishes a tracker-triggered quest, release and progress reads include
trigger.tracker, which older SDKs reject because the trigger schema is strict: upgrade participant apps before publishing one. Hosted-app keys holdingest, but responses (the minted key,access()) keep listing onlyreadandpoints, so older CLIs, API Workers and builders still parse them. Permission lists inaccess()projects and hosted-key responses now drop values this release does not know instead of rejecting the response. The database migration runs automatically. -
ea5d69e: Quests can check token balances of a member's linked wallets.
- Evaluators:
ctx.wallet.balance({ chain, token?, address? })reads SOL and SPL tokens (including Token-2022) on Solana mainnet, and native and ERC-20 balances on any EVM chain inevmChains(the EVM networks dRPC serves, by CAIP-2 ID such as"eip155:8453"). It reads all of the member's pinned linked wallets of that family, or one of them, and returns integer amounts with the token's decimals, captured for replay. Each read is one of the evaluation's eight capability calls. - Authoring:
parseUnits()andformatUnits()convert whole-token amounts, andWalletBalanceQueryandWalletBalancetype the call. - Contract:
evmChains,evmChainSchemaandwalletBalanceSchema. - Management: the member detail's
balanceCheckslists the member's recent recorded balance reads; Console shows them under the member's wallets. - Snippet:
token-holderchecks that a member's linked wallets hold at least a minimum.
Upgrading. Nothing breaks. Deploy the backend and API Worker before publishing quests that call
ctx.wallet.balance(). EVM chains need the platform'sDRPC_API_KEY; without it, EVM balance reads fail the verification with "EVM balance checks are not configured". Solana reads need no new configuration.evmChainsis generated from dRPC's chain list: a later release that removes a chain breaks quests that read it, and its changeset will say so. - Evaluators:
-
e7853fb: Participants can sign in, and sign up, with a Solana, EVM or Provenance wallet where a project enables it, and apps can draw generated avatars.
relay.auth.wallet.signIn(signer), and its two stepsstart(input)andverify(proof).relay.auth.providers()returnswallets, the networks the project enabled.- React:
useSignIn().withWallet(signer),useSignInMethods().wallets,useSignInWallets(networks)(the installed wallets that can sign in on those networks), anduseMe(). The starters offer installed wallets next to email and OAuth. relay.me.get()returns the signed-inmemberand anavatarSeedderived from their first wallet without revealing it.defaultAvatar(seed)anddefaultAvatarSvg(seed)draw a deterministic avatar (DiceBear Shapes, CC0);walletAvatarSeed(wallet)seeds a member's own wallet list.- Contract:
walletSignInNetworkSchema,walletSignInNetwork(),isWalletSignInRoute()andmemberSummarySchema. - Management:
participantSignIn()andconfigureParticipantSignIn({ wallets })(publish) read and set the project's wallet sign-in per environment;setMemberWalletSignIn(member, { chain, address, enabled, reason })(review) turns sign-in with a member's wallet on or off. Console has both under Settings → Access and on the member.
Upgrading. Nothing changes until a project enables wallet sign-in. Deploy the backend and API Worker first; the database migration runs automatically.
/v1/auth/providersgainswallets, which older SDKs ignore. Apps that proxy throughproxyParticipantforward/v1/auth/wallet/*and/v1/meafter upgrading the SDK. A platform without OAuth providers or email no longer answers 503 for projects that enable wallet sign-in. -
b22ead4: X trackers can trigger quests, and budgets can reset weekly.
automatic({ tracker })accepts an X tracker. The quest runs once for each post the tracker collects for the first time; refreshes, metric changes and edits never run it again.beneficiary: "author"(the default) pays the member whose selected, verified X account wrote the post;beneficiary: "replied-to"pays the member whose selected, verified X account the post replies to."replied-to"on a custom tracker throws.ctx.tracker.xcarries the captured post (contentandevidence) for X trackers, andctx.tracker.attribution.accountcan be{ provider: "x", issuer, subject, revision, linkedAt }.budget({ period: "week" })limits completions or points per ISO week in UTC, starting Monday, by the attempt's eligibility time.- New trigger outcome reasons: "Post is not a reply", "Reply is to the author's own post", "No member has linked this X account" and "Captured post is unavailable".
Upgrading. Nothing breaks for existing projects. Release and progress reads that include an X tracker trigger (
source: "x-search",beneficiary: "replied-to") or aperiod: "week"budget are rejected by older SDKs, whose schemas are strict: upgrade participant apps before publishing either.
0.24.0
No changes in this release.
0.23.1
No changes in this release.
0.23.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
-
49fa0fd: Apps can now match the ledger and tiered reward IDs that reads return to the keys they declared.
relay.pointLedgers.list()(GET /v1/point-ledgers) and the React hookusePointLedgers()return each ledger'sid,key, andname.relay.tieredRewards.list()(GET /v1/tiered-rewards) anduseTieredRewards()return each tiered reward'sidandkey.- Both reads are open to signed-out visitors. The hooks return the list in
items.keyis null for resources created in Console without one.
Reads name ledgers and tiered rewards by ID. Comparing a referral reward's, account's, or history entry's
balancewith a key such as"credits", or an entitlement'sreward.idwith the key given todefineTieredReward, never matched, and apps could show "200 ledger_… points". Find the resource by key, compare IDs, and display its name or title.Quest view rewards are now typed:
{ kind: "points", label, amount, ledger: { id, key, name } },{ kind: "tier", label, reward: { id, key, title }, tier }, or{ kind: "integration", label }. Labels read "10 XP" instead of "10 ledger_…". Tier labels show the reward title, followed by the tier title when the reward has several tiers, instead of the tier ID.Upgrade notes:
- Code that reads
labelkeeps working, and older SDKs ignore the new fields. Replace label parsing withamount. If you compared a tier reward'slabelwith its tier ID, comparetierinstead. - TypeScript code that builds quest views, such as test fixtures, must add
kindand that kind's fields. - Deploy the updated backend and API Worker before upgrading the SDK: this SDK expects
kindon quest view rewards. Apps that proxy participant requests withproxyParticipantorhandleHostedAppforward the new routes once they upgrade. - No database migration is required.
0.22.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
-
2614ad4: Add
ctx.imagesfor quest evaluators:inspect,compare(perceptual hash distances),review(AI judgement over up to four images),extract(typed fields read from a screenshot) andseen(other participants' near-duplicate images). Images are named by reference: the attempt's photo evidence, the linked X profile picture, an X post's attached image, or a reference image listed under the newimageskey inrelay.json(JPEG, PNG or WebP, up to 5 MiB each and 10 per deployment) and published withdomino deploy, which uploads new files directly to storage and sends only their hashes. New snippetsx-profile-picture,unique-photoandcommunity-screenshotinstall complete quests with operator-tunable similarity thresholds.ctx.x.post()results now include attachedmedia.Additive and backward compatible. Existing quests, including
ctx.review.photo(), keep working. To usectx.images, rebuild quests with this SDK version; bundles built with earlier versions do not pass it toevaluate. Deployingimagesneeds a server with this release; older servers ignore the key, and quests that reference those images then fail verification with "Image … is not published".
0.21.0
Not published to npm. Its changes first reached npm in 0.23.1.
No changes in this release.
0.20.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
-
2ff82fb: Remove
reviews.listandmembers.listfromcreateRelayClientand theirReviewListOptionsandMemberListOptionstypes. Since the 0.10.0 release the participant API no longer servesGET /v1/reviewsorGET /v1/members, so these calls fail with every SDK version; removing the methods turns that runtime failure into a type error.Upgrade notes: working projects are unaffected, because these calls could not succeed. If your code still references them, delete the calls. List members from a server with the management client:
createManagementClient(...).page("members", { q, limit }), ormember(id)for one member. Review pending attempts in the Domino Console Activity page. Members can still download their own evidence withevidence.download.
0.19.0
No changes in this release.
0.18.4
No changes in this release.
0.18.3
No changes in this release.
0.18.2
No changes in this release.
0.18.1
Patch Changes
-
2186fe3:
domino devno longer breaks participant requests when--api-urlis Console's management proxy (https://console.domino.run/relay). The CLI used to pass that URL to the app asDOMINO_PARTICIPANT_API. The participant proxy refuses any upstream with a path, so every/relay/*request answered 503 "Participant API is not configured".The API now reports its participant origin in
/preflight(participantApi).domino devsends participant requests there and gives the development management key the same origin, as deployed builds receive it. With an older API, the CLI uses--api-urlif it is already an origin, then relay.json'sapiUrl. If neither is, it stops with instructions instead of starting a broken preview.domino doctor --remotehas a newparticipant-apicheck, and reports the resolved origin asscope.participantApi. The check fails when no participant origin can be found, or when relay.jsonapiUrlis not that origin, which staged builds rely on. The proxy's 503 message now says to use the API origin rather than a management URL.Upgrade note: if relay.json
apiUrlis set to a management proxy URL, change it to the participant API origin (https://relay.domino.run).
0.18.0
Minor Changes
-
bf84375: Add
trackers.content(reference, { id, observation })to hosted quest evaluators. The read returns a retained X body and verified account attribution to the attempt's member, captured together for replay. Engagement counters, mutable author profiles, usernames, and verification badges do not determine ownership. Explicit disconnects and ambiguous legacy connections return unlinked attribution.Management clients expose the same read with a required
memberin the query andreadpermission. Deploy the updated backend and API runtime before using this method. Existing tracker methods and response shapes remain unchanged. This adds evidence reads, not automatic admission or engagement rewards. -
ac63da2: Add automatic X content quests with
content(tracker, policy)andperContent(), pinned content context, quest enrollment, historical claims and staff approvals, and paginated admission explanations. Existing trigger and execution contracts remain readable. Upgrade the backend and collector workers before publishing content quests; update participant apps to use the new SDK proxy paths. Existing tracker matches are not backfilled, and edits after admission do not reevaluate or reverse prior rewards. -
0a0ac9a: Add independent daily engagement rewards for qualified X content. Author immutable cumulative formulas with
defineEngagementReward, publish bundled code withmanagement.engagement.publish, and read settlement history throughengagement.page. Partial budgets retain paid and permanently excluded growth separately.Deploy backend migration 0060, the API formula runtime, and workers before publishing engagement rules. Existing quests keep their contracts. This first version supports new discoveries and historical initial baselines; CLI manifest deployment, formula replacement, explicit backfill, enrollment baselines, and automated reversals remain unsupported.
-
40c9761: Add opt-in X evidence references to tracker records. Pass
includeEvidence: trueto receive a stable content revision and a separate provider observation identity. Engagement and author profile refreshes keep the content revision unchanged. Custom records returnevidence: nullwhen requested; default responses keep their existing shape.Deploy backend migration 0056 and the updated API before requesting evidence from the new SDK. The migration backfills retained X observations and captures writes from previous backend versions during rolling deployment. Existing SDK callers need no changes. Automatic content qualification and engagement rewards remain unimplemented.
-
b596a0a: Add content-only performance gates with declared engagement metrics and a configurable freshness limit. Content approval stays cached while new samples run performance. A body edit or change to the verified account selection requires fresh content approval. Missing or stale metrics wait without running the callback. The first passing performance decision resolves the content-approved reward selection; later metric changes cannot duplicate awards or retry budget exclusions.
Use
content.performance({ quest, content, member?, before? })to read current eligibility and retained metric evaluations. Existing content qualification history remains content-only. Performance callbacks receive the declared metrics, sample time, pinned content, member, and settings. They gate rewards but cannot replace the content-approved selection.Apply migration
0059_content_performanceand upgrade the backend, API evaluator, and workers before publishing quests with this stage. Older evaluator runtimes cannot dispatch performance safely. Existing quests need no source changes and omit the new optional fields. Upgrade the SDK to author performance stages or read their history. Automatic reversals and daily engagement settlement remain unsupported. -
e5b7091: Reevaluate admitted X posts after body edits and expose
content.qualifications({ quest, member?, content?, before? })in participant and management clients. History keeps the original release, beneficiary, and evidence, with qualification state separate from the one-time reward outcome. Stale evaluations and reviews cannot award rewards; later qualifying edits cannot repeat rewards or retry a budget exclusion. Existing admission APIs retain their response shapes. Removal and rejection do not reverse prior awards.Apply migration
0058_content_qualificationand upgrade backend and workers together before enabling content quests. Older decision workers cannot enforce stale-content guards during mixed-version deployment. Existing admissions are retained and backfilled; no quest source changes are needed. Upgrade clients to use the new history method. -
243d9e7: Add explicit
engagement: [{ entry: "engagement.ts" }]manifest entries, with optionalexportName, todomino buildanddomino deploy. Preview and publish engagement rules with their tracker, qualifying quest, and points balance in one project transaction. Previews report new, unchanged, and changed formulas; formula changes preserve past earnings and budget spend through observed boundaries.Resume the saved deployment ID to publish a reviewed preview or recover a committed result. Stale engagement changes reject the entire deployment. Missing entries do not delete rules, and unchanged code does not create another scoring version. New rules use actual publication time as their discovery cutoff.
Upgrade the backend and settlement workers before deploying engagement entries. Manifests and SDK requests without engagement entries keep their request shape, and existing hosted-build activation receipts remain replayable. CLI publication remains test-only; live previews remain available. Changes to metrics, budgets, or qualifying quests remain unsupported.
-
4cea332: Add preview/apply management workflows for engagement formula changes, immutable version history, and participant boundary history. Each post settles its governing formula through an available fresh eligible observation before the latest requested formula starts at the same counters. Existing budget spend and exclusions survive; superseded unapplied formulas do not invent intermediate earnings.
Preview totals describe retained accounting, not promised future payouts. Available observations may predate the request by up to the rule's freshness limit. Quest, balance, metrics, baseline policy, freshness policy, and budgets cannot change in this workflow.
Deploy migration 0061 and upgrade/drain all settlement workers before requesting a formula change. Older workers do not enforce version boundaries. Existing settlement response shapes remain compatible; histories may now include an additional formula-boundary settlement in the same UTC day.
Engagement creation and formula apply now follow the existing live-publication policy: management keys can publish in test, while live publication requires a Console session.
-
2a7ca88: Add an optional
contentfilter tocontent.admissions({ quest, content })for direct post lookup. Management and participant reads retain their existing project and member isolation, response shapes, and unfiltered pagination behavior. Upgrade the backend before sending the new filter; older servers reject it. Existing callers need no changes.
0.17.2
Patch Changes
-
664eb80: Share current X post content, author verification, and engagement across matching trackers in the same project and environment. Each tracker keeps its own matches and first-seen times. Concurrent collectors coordinate known-post refreshes, and captured quest reads retain their original results.
Pausing a tracker stops its collection; another active tracker can still update their shared posts. Custom datasets remain isolated per tracker. Existing tracker definitions and SDK calls need no changes.
Requires backend migration
0055_shared_tracker_contentand the matching backend deployment. The migration retains the previous release's write format for rolling upgrades and preserves existing observation history. Removing the legacy payload columns requires a later contract release after all old workers have stopped. Old workers may still perform duplicate refreshes until the rollout finishes.
0.17.1
No changes in this release.
0.17.0
No changes in this release.
0.16.1
No changes in this release.
0.16.0
Not published to npm. Its changes first reached npm in 0.16.1.
Minor Changes
-
abc6358: Add
ctx.review.text({ text, criteria })with typed pass, fail, and unclear results for authored quest decisions. Review verified content such as X tracker posts using the existing backend AI configuration. Validated results are captured and reused across retries; provider failures remain errors.The capability limits text to 16,000 JavaScript string units and criteria to 2,000, with a 12-second provider timeout. Review text is treated as untrusted evidence. Credentials stay server-side. This additive API requires a backend and evaluator bridge deployment that supports text review before publishing quests that use it. Existing photo quests remain compatible. Combine this capability with
evidence.text()for submitted text orevidence.url()plus verified X post reads. -
3280aa1: Add required or optional text and HTTP/HTTPS URL submissions with configurable text lengths and exact allowed hosts. Evaluators receive captured strings through ctx.input.value. Browser handles and campaign controls preserve the original values across interrupted delivery and evaluation retries. Backend admission enforces the published contract and returns correctable validation errors.
Submitted URLs never trigger automatic fetching. Use
xPostIdandctx.x.post({ postId })to verify a post through the configured X provider; reads are captured for retries. Participants link X before verification. Compare the returned author ID withuserIdfromctx.x.connection()to establish authorship.Existing photo and quiz request shapes and saved photo records remain compatible. Upgrade the SDK and CLI together before authoring text or URL quests. Existing participant apps must add controls and declare text and url in supportedInteractions; newly exported campaign starters include both. Deploy the updated backend and API Worker before publishing these quests. Text and URL storage need no additional migration; the integrated X capability requires the X execution-snapshot migration.
-
074c292: Add captured X verification to authored quest evaluators:
x.connection(),x.profile(),x.post({ postId }),x.follows({ userId }), andx.reposted({ postId }).Participants link their X accounts through the existing account flow. Platform operators configure the backend's
X_API_WRAPPER_API_KEY, shared with trackers. Account changes invalidate unfinished reads; retries reuse captured evidence. Existing quests remain compatible. Apply the additive X execution-snapshot migration and deploy the updated backend and evaluator Worker before publishing quests that use the new methods, and update the SDK and installed CLI references.
0.15.0
Minor Changes
-
6c309ea: New projects can start from a choice of starters: Campaign tour (the default), Classic questboard, Waitlist, Referral program, Discord activation, and API. In Console, choose one under Start from when creating something new. In a terminal, run
domino create NAME --template waitlist, or answer the new prompt. Scripts without--templatestill get the campaign tour.starterTemplatesandstarterIdSchemain@domino-sdk/relay-clilist the offered starters. Projects now report the chosen starter as a top-levelstarterfield, andPOST /repositoryaccepts any offered starter ID. Omit it to use the starter chosen at setup. At creation, Domino writes the project name to the starter'ssrc/project.jsonand places the brief where the starter'sCAMPAIGN.mdmarks it.@domino-sdk/relay/reactprovides the React layer the starters share:RelayProvider,useExperience,questGroups,useQuest, and the quest status helpersuseSignIn,useSignInMethods,useSignOut, anduseAccountsuseLeaderboard,useReferralSummary,useInvitation, anduseRelay
React 19 is an optional peer dependency.
@domino-sdk/relay/viteaddsserverBoundary(), which fails a Vite build when browser code importsserver/or@domino-sdk/relay-cli.@domino-sdk/relay/portal-proxyaddshostedAppRequestandhandleHostedAppfor the Domino routes of a hosted app's Worker.Upgrade notes: nothing breaks for existing projects. Apps built from the earlier campaign starter keep their own
src/relaycopies and can switch to@domino-sdk/relay/reacthook by hook. Wrap the app inRelayProvider, which replacesExperienceProviderandsrc/relay/client.ts, then change imports. The hook names and behavior are unchanged, except thatuseRoleDeliveriesis now internal anduseExperience()also returnsmember.
0.14.1
No changes in this release.
0.14.0
Minor Changes
-
11abaf5: Add a bounded X search preview for exploring tracker queries before defining them in code.
management.trackers.preview({ query, since })returns at most 100 matching posts from 3 search pages, up to 30 days back, with author verification retained andmoreset when X had further matches. It requirespublishpermission, is rate limited, stores nothing, and never publishes a tracker or starts collection. Console's Trackers page uses it to show a sample and copy a tracker definition or coding-agent prompt.Requires the matching backend deployment. Existing clients and manifests are unaffected.
0.13.0
Minor Changes
-
e969f1e: Add maintained tracker datasets with a managed X search source, preserved author verification, read-time filters, and custom indexer ingestion. Publish definitions through relay.json and the existing deployment preview flow. Query datasets from the management SDK or captured read-only capabilities in hosted quest evaluators.
Requires the matching backend migration and deployment before publishing or reading trackers. X collection requires the platform's X_API_WRAPPER_API_KEY credential. Existing project manifests remain valid. Custom evaluator test contexts that explicitly implement EvaluationContext must add a trackers reader. Collection does not automatically issue or reverse rewards.
0.12.0
Minor Changes
- 9fdc113: Reward deliveries now end.
rewardDeliverySchema.statusadds a terminalfailedstate, reached when Discord permanently rejects a role grant (unknown role, member left the server, missing permissions) or transient errors persist for about a day. The management client addsretryRewardDelivery(id), which moves a failed delivery back topendingand starts a new delivery round. Clients that parse deliveries with an older SDK rejectfailedrecords, so upgrade before reading deliveries.
0.11.0
Minor Changes
-
5337971: Add
streak({ days, timeZone })for automatic observation quests. Verified actions count once per community day, including daylight-saving boundaries. Late evidence repairs continuity; milestone rewards commit once with progress. Participant reads expose current/best runs, qualifying days, deadlines and reward status. Timezone changes take effect at the next community boundary without rewriting history.Each quest awards one lifetime milestone. Configure separate quests for separate goals. All streak quests in a project/environment share one timezone. An existing goal length is immutable, and budget-exhausted milestones keep their evidence without later automatic payout.
Apply the backend migration and deploy the updated backend, SDK and participant app before publishing streak quests. Existing once/keyed quests retain their behavior. Older SDKs cannot parse the new
streakcompletion kind orprogressedattempt status; upgrade clients that read a project containing streak quests.
0.10.0
Minor Changes
- 8a81fde:
proxyParticipantnow sends the app origin to the shared API by default, ashosted: truedid. The shared API has no deployment-wide participant project, so calls without it failed with 503 and now get 404 “Participant requests must come through a registered app or identity integration”. Existing code that passeshosted: truekeeps working and can drop the option. Passhosted: falseonly for an upstream that applies its own project scope.
Patch Changes
- d5d530d: Mark the staff and operator methods on
createRelayClientas deprecated:reviews.list,members.list,lookupMember,snapshot,template,journeyTemplate,publish,publishBatch,beginHandover,confirmHandover,decideandme.linkPass. The participant API accepts only member sessions, so these calls already fail. No upgrade is needed; move any remaining calls to the management API,domino deploy, or a server-side pass verifier that callsPOST /management/v1/passes/verify.
0.9.1
No changes in this release.
0.9.0
Minor Changes
-
6a5b6d3: Remove the offline development runtime and the
dev --offline,dev --review-mode, anddoctor --offlineoptions. Usedomino devwith the hosted test environment and real sign-in. New campaign exports include only the SDK and CLI packages.New release and catalog publication accept only Workers AI. Historical provider identifiers remain readable for retained records, but cannot select simulated evaluation.
0.8.0
Minor Changes
- 25ea110: Assign persistent resource IDs on the server. Authoring declarations now use stable keys, project and draft creation use separate retry keys, and point ledgers must be created or declared before use. Existing IDs remain valid. Legacy authoring
idremains supported as a key alias. Usedomino migrateto preview a repeatable source and manifest upgrade, apply with--write, and inspect actionable diagnostics withdomino check --json. Creation helpers generate retry keys, retry uncertain responses once, and expose the retry key onCreationError; raw HTTP creation still requires an action ID. Consume returned IDs for newly created resources.
0.7.0
Minor Changes
- 0bbefc1: Add point ledger metadata with stable IDs, editable names and descriptions, management list/save methods, and a pointLedger setting for Console pickers. Existing balance keys retain their accounting history.
0.6.0
Minor Changes
- 5cd39a2: Add observation-triggered quests, keyed completions, visibility, shared atomic budgets, and backend observation submission. Include an automatic Discord announcement reaction snippet and participant guidance for repeatable quests.
- 5cd39a2: Add Discord message contributions, moderator recognition with separate actor and beneficiary identities, richer member and scoped message reads, and durable Discord role rewards with reusable role selectors and delivery status. Include three distinct capability snippets and updated authoring guidance.
- 5cd39a2: Add reusable integration resource selectors and date/time settings. Include Discord channel selection, scoped resource lookup, publication validation, and paused publication for unconfigured resource settings.
0.5.0
Minor Changes
-
85bb8aa: Add compact prefixed resource ID generation and expose globally unique project slugs. Hosted builds carry the slug used for stable staging addresses; development previews use the slug and a shorter workspace hash. Existing resource IDs remain valid.
-
c24042e: Remove the Phantom demo sign-in helper and campaign-specific API routes from the platform. Local authentication defaults to the generic local/campaign test scope. Event pass admission requires a verification integration instead of accepting Breakpoint-formatted codes.
Expose a generic management operation for app-owned pass verification. It requires confirmation permission and keeps unique pass ownership and retry-safe entry submission in Relay.
-
990c756: Support compiled Cloudflare Worker apps in connected previews and cloud staging, including SSR, server actions, and API routes. Add the worker app configuration and private module artifacts while preserving static hosting.
Add opt-in
app.routing="transparent": app requests and responses pass through unchanged, with no reserved routes. Move the starter participant proxy into application code and add an explicit hosted participant integration option. Omitted routing retains legacy behavior for existing projects, deployed builds, preview sessions, and older CLI requests. New starters opt in; existing apps migrate explicitly after adding their own participant handler.
0.4.0
Minor Changes
- d8a2572: Add project integrations with managed Discord bot installation in Console. Quests declare integrations and read membership through ctx.discord.member() without a server ID. Participants use basic Discord identity sign-in or linking. Capture identity and integration revisions for recovery, provide starter identity controls and failed-submission discard, and include the membership snippet and operator/agent guides.
- 8ead9ee: Add project-scoped points leaderboards with code-defined calculations, reviewed publication, participant rankings, and audited points updates. Support period windows, shared ranks, participation groups, and board exclusions.
- 1fb2edd: Add code-defined referral programs, session-bound invitation attribution, qualification rewards, milestones, and selective inviter point bonuses with correction recovery. Include participant and management APIs, project publication, Console controls, and agent references.
Patch Changes
- c475d40: Use the complete non-quest deployment schema for hosted build artifacts, preserving leaderboard and referral modules during preview activation and starter verification.
- 7204e04: Discover default-exported quests and quest types using patterns in relay.json. New projects include discovery patterns, and explicit entries can override deployment settings. Reject missing selected exports while bundling. Add editable photo, quiz, and staff-confirmation recipes through the public snippets registry.
0.3.0
Minor Changes
- 53d6aef: Support existing application logins through a server participant adapter and project-scoped identity integration keys. Add Console provisioning, rotation and revocation, trusted participant routing, and an agent integration reference.
0.2.0
No changes in this release.
0.1.0
Minor Changes
- d46d47c: Introduce the Relay SDK, Domino CLI with bundled agent skills, and standalone local runtime.
CLI
1.66.1
Patch Changes
- d77e195: Repeated refused credentials and unknown paths from one address are now refused with 429. After an API server has refused one address (an IPv4 address or IPv6 /64) 60 times within 10 minutes for a missing or refused credential, a Git path that names no repository, or a path the API does not serve, it answers that address's management API, MCP and Git requests and unknown paths with 429
rate-limited, message "Too many refused requests. Try again later.", andRetry-Afteruntil the 10 minutes end, before checking any credential. A personal key or MCP token counts only when Clerk does not recognize it. Successful requests, Git's first credential-less request of a fetch or push, an MCP request without an MCP token, and a hosted app's own server calls never count. The CLI reports the wait asdetails.retryAfter; the troubleshooting reference explains how to recover. - @domino-sdk/relay@1.66.1
1.66.0
Minor Changes
-
165ca99: Harder sign-in.
domino logincodes now expire after 15 minutes, not an hour. Console's approval page shows when the request was made, the country of the IP it came from and the CLI's version and OS, and warns to approve only a code from your own terminal. The CLI sends that description as itsUser-Agent. One client network (an IPv4 address, or an IPv6 /48) holds at most 20 pending sign-ins.- Email sign-in codes name the app. The email says which app and host the code is for, and to enter it only there. Only the latest code for an address works: an earlier one answers
code-expired. Ten wrong codes for an address on a project in an hour, or twenty in a day, refuseemailVerifywithrate-limitedandRetry-Afteruntil the window ends, whatever new codes it gets. An address gets at most ten codes an hour from every app together; past that,emailStartanswersrate-limited. - Plugin OAuth connections finish in Console, as the person who started them (experimental). The vendor's callback no longer connects by itself: it sends the browser to Console with a one-time
oauthCode, and the newmanagement.connections.completeAuthorization(plugin, { code })redeems it within five minutes, for the same person, project and environment only (403scope-mismatchfor anyone else, 409oauth-flow-expiredfor a spent or expired code). WithoutreturnTo, the browser returns to Console's Integrations page for the project.authorizenow refuses automation, service and hosted app keys with 403credential-not-allowed. New experimental exports:connectionAuthorizationCompleteInputSchemaandConnectionAuthorizationCompleteInput.
Patch Changes
- Updated dependencies [165ca99]
- @domino-sdk/relay@1.66.0
1.65.0
Minor Changes
- 9a37fe5: Clear test data in hosted app storage.
domino storage clear sql DB --table tickets(or--all-tables --keep settings) empties SQL tables and keeps their schema andd1_migrations;domino storage clear kv PHOTOS --prefix photo:(or--all-keys) deletes KV keys.--dry-runcounts first, and without--yesthe command asks in a terminal and refuses unattended. The management API has the same operation asPOST /management/v1/storage/{kind}/{name}/clear, andcreateManagementClientasstorage.clear(kind, name, input). It works only in the test environment: live refuses withtest-environment-required. It needspublish, and each clear is audited. A KV clear deletes at mostlimits.apps.storageClearKeys(10,000) keys a call and answerscomplete: falsewhile keys remain; the CLI repeats until none do. Test member reset, purge and erase still leave app storage alone.
Patch Changes
- fabbf60:
domino doctorlabels a Worker app's preview by its kind. Whenrelay.jsondeclaresapp.kind: "worker", the--jsonresult now haspreview: "worker-app-configured"instead of"static-app-configured". Static apps and projects without anappkeep the value they had. - 7820fec: The Domino skill's campaign testing reference explains Hide toolbar in the staging toolbar's
⋯menu: it hides the toolbar for the browser tab, leaving a small restore button in the corner, and while it is hidden--domino-staging-toolbar-inset-bottomis0pxand<html>has nodata-domino-staging-toolbar, so screenshots and layout checks see the live layout. - a09e24b: The skill's schedule dispatch guarantees say what a run's
errornow records. A run whose build has no Worker fails, so the next tick still runs. An uncertain run names what Relay saw: the dispatch bridge's HTTP status and cause, no answer within the timeout, or the error that interrupted the send. An exception the app throws before answering is recorded there too, trimmed to 300 characters, so keep secrets out of thrown messages. - Updated dependencies [9a37fe5]
- @domino-sdk/relay@1.65.0
1.64.0
Minor Changes
-
0a4d580: Domino now deletes the evidence members submit (photos, text, links and quiz answers) 90 days after the attempt is decided, in test and live, unless you change it. Nothing was deleted when this shipped: evidence that already existed is first deleted 90 days after the change reached Domino, on 18 January 2027 for projects that existed on 20 October 2026, and 90 days after creation for newer projects. Attempts waiting for a decision keep their evidence. Each project's date shows in Console's project settings and in
noticeEndsAt.To keep evidence longer, or until you delete it, change the period in Console's project settings, with
management.attempts.setEvidenceRetention({ evidenceRetention: { days } })(1 to 3650 days) or{ evidenceRetention: "keep" }, or withdomino attempts set-evidence-retention --days Nor--keep. Changing it needspublish; shortening it, or switching from keep to days, also needs an organization admin or a service key.management.attempts.evidenceRetention()anddomino attempts evidence-retentionread it. Both methods are experimental. Each change is recorded in the project's access history, and each deletion pass as anevidence-redacted-beforeevent with no actor and no quest. To delete sooner, or per quest, keep usingattempts.redactEvidenceBefore. -
0b1f8de: Operators can ban a member, and optionally take back what they earned. While banned, a member's attempts, completions and invitation acceptances are refused with 403
member-banned, their invitation code answersinvitation-not-found, attempts waiting for a decision are rejected, tracker records that would count for them are skipped withmember-banned, leaderboards and settlements leave them out, and referral payouts to and from them stop.relay.me.get()now answerseligible, false while the member is banned (true from older Relay versions).management.members.ban(member, { reason, rollback })bans, whererollbackcan revoke all their completions, reverse the referral payouts they received, and reverse manual awards, imported opening balances and engagement payouts, a step at a time in the background.management.members.previewBan(member, { rollback })previews it per point ledger, andmanagement.members.unban(member, { reason, restoreCompletions })lifts it. Banning and unbanning needreviewpermission; reversing referral payouts or point awards also needspublish.members.getanswers the member's latest ban asban, and member lists answerbannedAt. The CLI addsdomino members ban(with--rollbackand--dry-run) anddomino members unban. Member bans are experimental. Member imports refuse rows naming a banned member withmember-banned.
Patch Changes
- 166254b: App login now works on a private staging address.
createParticipantBackendno longer sendsX-Relay-App-Originwith its proxied calls, and Relay selects a request that carries an app login key by the key even when it also names an app origin, so a Test key registered forhttps://<slug>--staging.domino.pagesigns members in there without a workaround. Apps on SDK 1.63.0 work without upgrading. Thestaging-access-requirederror reference says a request with an app login key needs no staging pass. - 28acc4c: The management skill reference says Console's Integrations page shows a card per plugin that opens a panel with its connection form.
- 59cf5db:
management.identityIntegrations.createIntoAppSecret(input, name)andaddSecretIntoAppSecret(id, name)create an app login key, or add a secret to one, and store the new secret as the hosted app secretnamein the key's environment instead of returning it. The response hasappSecretand notoken. They need publish permission, and like every app login key change they work only from an organization administrator's Console session. When the app secret is refused, no key or secret is created. The CLI skill reference and capabilities describe Console's matching "Also save it as an app secret" option. - 15b249d: Agents can act as test members on a
domino devpreview, before staging.POST /management/v1/staging/linktakes an optionalpreview, the preview URLdomino devprinted, and returns a one-minute link on that preview that opens the test toolbar for the same operators as a staging link. The session works only on that preview host and only while it runs; the dev server never receives its cookie, and other visitors to the preview see no change. A URL that is not a running preview of the project answers 404preview-not-found. The Domino skill's campaign testing reference adds the steps under "Check participant flows on staging", and the hosted reference points to them for checking the participant flow before staging. - Updated dependencies [166254b]
- Updated dependencies [ff6c976]
- Updated dependencies [0a4d580]
- Updated dependencies [59cf5db]
- Updated dependencies [7bd9a79]
- Updated dependencies [0b1f8de]
- @domino-sdk/relay@1.64.0
1.63.0
Minor Changes
-
8c4b0c0:
domino checkoutaccepts a project's slug, asdomino whoami --jsonlists it, as well as its ID; a slug two organizations share needs--organization, and an unknown project now fails withSCOPE_NOT_FOUNDbefore any repository request. Without--json, checkout shows Git's fetch progress, and a fetch that moves under 1 KB/s for 60 seconds fails withCHECKOUT_GIT_FAILEDinstead of waiting on a dead connection. Without--json, an invalidrelay.jsonnow lists each invalid field and its message under the error, asdomino doctordoes; the--jsonerror is unchanged. -
052744c:
inspectIntegrationKey({ baseUrl, integrationKey })from@domino-sdk/relay/portal-proxyanswers which app login key adi_key is: itsid,name,organization,project,environment,origin,identityKind,vouchesAccounts,secretExpiresAtandappOrigins, the addresses Domino serves the project's app from in that environment. It calls the newGET /v1/auth/integration, never returns a secret, and does not count as a use of the key. An unknown, expired or revoked key throwsParticipantExchangeErrorwith 401invalid-credential.identityIntegrationKeySchemaand theIdentityIntegrationKeytype are exported from@domino-sdk/relay/server.Behavior change:
domino secrets setnow checks a value that starts withdi_before storing it. It refuses, storing nothing and exiting 2 withINVALID_ARGUMENT, a key that is unknown, expired or revoked, or that belongs to another project or environment, such as a Live key set as Test's secret. It stores a key whose origin is not the app's own address for that environment with a warning, and a key it could not check (network failure, or a Relay without the route) with a warning. A checked key is named in the result's newidentityIntegrationfield. Other values are stored as before. -
eaaae2a: Test members that
resetrefuses because they redeemed an offer, hold a reservation or a started reward handover, or took part in a referral can now be purged:management.testMembers.purge({ actionId, members, reason })anddomino test-members purge MEMBER... --reason TEXTdelete up to 500 test members in one transaction, in the test environment only. The stock unit each redemption, reservation and pickup handover took goes back to its stock item, so the offer's stock and redeemed count stay consistent, and the members' referrals and referral accounting go with them. List both sides of every referral: one with a member outside the list refuses the call withtest-member-reset-blocked(referrals affect another member, naming the unlisted member inoutside). Every other reason reset refuses still applies, and a refused purge changes nothing. The audit records one event with the operator, the members, the reason and the stock returned.resetis unchanged. See Purge members reset refuses. -
69e017d: Erase a member on the person's request:
management.members.erase(member, { reason, actionId? })anddomino members erase MEMBER --reason TEXT [--yes](POST /management/v1/members/:member/erase,publishpermission, live and test). It can't be undone. The member's profile and uploaded avatar, sign-in identities, linked accounts and their tokens, app identities, wallets, sessions, consent, leaderboard aliases, referral code, the evidence of every attempt and the stored content of the tracker records their accounts authored are deleted; reasons people typed on the rows that stay read[erased]. The member ID keeps its points, completions, rewards and referrals. The result is{ member, erasedAt, deleted }; while an attempt waits for a decision or a reward handover is in progress it is refused with 409member-erase-blocked. See Erase a member on request.Member reads (
members.get,members.list) carry a newerasedAton an erased member, andmembers.getanswersprofile: nullfor it. After erasure, writes naming the member (points, badges, groups, campaigns, consent, profile changes) are refused with the new 409 codemember-erased, records naming its ID attribute to no one, and the same person signing in again becomes a new member. A member import's revert passes over an erased member's row and reports it inkeptLinkswith reasonmember-erased. The CLI command asks for confirmation in a terminal and needs--yesotherwise. -
bbc3623: Klaviyo is the first destination plugin: connected through Klaviyo's OAuth, it keeps opted-in members' Klaviyo profiles current with
domino_properties (points, ranks, quests completed, unredeemed rewards, campaigns), subscribes and unsubscribes them as their email consent changes (a backfill or resync never resubscribes someone Klaviyo has unsubscribed), and records Domino's events asDomino …metrics. It never calls Klaviyo's send, campaign or flow APIs. See the new guide, "Send reminders from Klaviyo".management.connectionsgains, experimental like the rest of it:connect(plugin, { config, dryRun: true })connects a destination that supports a dry run, in the test environment, without secrets or the vendor's account. It records what it would have sent instead of sending it.Connection.dryRunandPluginInfo.dryRunsay which. Refusals: 409dry-run-not-supported,dry-run-test-only.activity(plugin)reads a destination's delivery record: last sync, failing state, members synced and skipped per reason, the latest backfill or resync with its progress, and the latest 20 errors.resync(plugin)sends every member's profile and attributes again, never past events; one runs at a time.dryRunLog(plugin, { cursor, limit })pages through a dry run's requests, newest first.
These answer 409
not-a-destinationfor other plugins. The CLI addsdomino connections connect --dry-run,connections activity,connections resyncandconnections log. -
9cb68e2: A text setting can be secret:
settings.text({ label, secret: true }). Operators set its value in Console or withmanagement.quests.updateSettings, and the quest's code reads it as usual, but no management response returns it. Releases leave secret settings out ofvaluesandconfiguration, and list the ones that hold a value in the new optionalwithheldSettings. Sodomino deploy --json,--preview --json,quests list,releases get, deployments, attempts and member reads no longer print a shared code a guest must not see. Console shows a secret as Set or Not set, with a field to replace it and Clear. A secret setting takes no default: one with a non-emptydefault, or a value from a deploy entry'ssettings, is refused, and reusable quest types refuse secret settings. A secret's entry in a release'sfieldssayssecret: true. Settings withoutsecretbehave as before. Withholding follows each release's own fields, so releases and deployments from before a setting became secret still show its old value: set a new one after marking an existing setting secret. -
8ee88a9: Test members can redeem an offer:
management.testMembers.redeem(member, { offer }), with the offer's key or ID, anddomino test-members redeem MEMBER OFFER. The redemption runs as a signed-in member's would, with the same price, stock, per-member limit, requirements and refusals, writes the same redemption and entitlement, answers the redemption, and replays by action ID. It works in the test environment only, and the audit names the operator. A test member with a redemption can't be reset. -
6fe6f22: The testing kit lists and replays every capability reader, and a few small defaults change.
capabilityCalls.runQuest,createTestContextandreplayAttemptresults gaincapabilityCalls: every capability call as{ sequence, request, outcome }, with an openCapabilityCallRequestwhosekindnames the reader. It includes the callscallsleft out: Telegram, NFT, contract call, delegation, wallet event and transaction reads, and the member's state.callsis deprecated.- Replay divergences of any reader.
replayAttemptreports a call of any reader the capture cannot answer in the newcapabilityDivergence, and stops there unlessfactsanswers it. Before, such a call of a newer reader failed the replay withTestSetupError; now the replay resolves withdecision: null.divergenceis deprecated, and isnullwhen the first divergence is a newer reader's call. - Tolerant replay. A captured answer with a field or an enum value a later Relay added, such as a new field on a Discord message, now replays instead of failing. Answers in
factsare still checked strictly. - Reader fakes.
fakeWalletReader(facts)andfakeTelegramReader(facts)from@domino-sdk/relay/testingbuild readers with every method Relay supplies, for tests that assemble their own evaluation context. The readers added after 1.0 stay optional in their types; quest code calls them with a non-null assertion, as the snippets now do (ctx.wallet.nfts!(…),ctx.telegram!.connection!()), instead of guarding against a runtime that lacks them. - Behavior change: Telegram trackers fire once per record.
automatic()on atelegram.reactions()ortelegram.messages()tracker now defaults toon: "new-record"instead of"new-observation", so a reaction removed and added again no longer runs the quest again. Published quests keep the mode they were published with; republishing one with this SDK switches it unless it setson. domino checkwarnsTRIGGER_REFIRES_ON_REFRESHfor a non-content quest on a YouTube Shorts or TikTok tracker that runs on every observation: each refresh evaluates it again for every video. Usecontent({ tracker })withperformancefor a metric threshold.RelayProvidertakesfollowWallet.followWallet={false}keeps a wallet sign-in's session when the wallet switches account or locks. The default,true, signs out as before.- Behavior change:
unauthenticatedends the session. A participant backend's (createParticipantBackend) 401unauthenticatednow ends the member's session in the SDK's experience, progress and activity observers, asnot-signed-indoes, instead of reporting an error.RelayErrorCode.Unauthenticatednames it. enrollmentonnot-enrolledskips. Tracker trigger outcomes,relay.quests.outcomes()and automatic outcomes carry an optionalenrollmentonnot-enrolledskips:"quest"for the quest's own enrollment,"campaign"for its campaign's. A campaign skip on a tracker trigger now says "Member has not enrolled in the quest's campaign".- Naming. A leaderboard's
balanceis the ledger it ranks and its points pool'sledgerthe one the pool pays into; both are documented on the type. New fields that name a ledger are calledledger; existingbalancefields keep their name in 1.x.
-
561f4ab: Tiered rewards (
defineTieredReward) can be listed, archived and restored.management.resources.list()anddomino resources listlist them in a newtieredRewardsarray (managedTieredRewardSchema), besideoffers, anddomino tiered-rewards listlists them alone. The experimentalmanagement.tieredRewards.archiveandunarchive(POST /management/v1/tiered-rewards/REWARD/archive|unarchive) anddomino tiered-rewards archive|unarchive KEY --reasonarchive and restore one in test, by key or ID, withpublishpermission; a promotion carries it to live, and Console Settings → Resources lists them with the same controls. Each returns{ resource, changed }, typed bytieredRewardLifecycleResultSchema. While one is archived, a deploy or promotion that would make a quest, offer or leaderboard prize newly grant it fails with 422tiered-reward-archived, naming it inreward. What already granted it keeps granting it, its entitlements and handover keep working, and its key stays taken. Console Rewards leaves archived tiered rewards out of its rewards. Promotions now list tiered rewards as definitions of kindtiered-reward, and a rollback archives one the earlier promotion did not carry. -
8d79fd6: Four operations that had two public paths with different results now agree, or say how they differ:
management.deployments.promote,submitandrejectare deprecated, removed in 2.0.0.promoteprepares a promotion of all of test and publishes nothing; its JSDoc now says so, and that the answeredidis the promotion's, read withpromotions.getand rejected bydeployments.getandpublish. Usemanagement.promotions.prepare()with a client for test, andpromotions.rejectto decline.submitandrejectact only on live deployments made before live refused them.domino deploy --promoteis deprecated in favor ofdomino promote --environment test, and prints a warning naming it.management.testMembers.acceptAnyway(member, { attempt, rules: "live", reason })applies liveattempts.acceptAnyway's rules in test:reasonis required, and an archived quest, a superseded attempt, a completion cooldown, an exhausted budget, an existing or revoked completion and a used key are refused with live's 409 codes, leaving the attempt and balances unchanged. The default (rules: "test") still accepts those, with no reason, so a tester can get a test member unstuck. Areasonis recorded with the decision either way.domino test-members accept-anyway MEMBER ATTEMPT --live-rules --reason TEXTsends it.GET /v1/reward-deliveriesandGET /management/v1/reward-deliveriesansweritemsbesidedeliveries, with the same rows, sopaginate((cursor) => relay.me.rewardDeliveries({ cursor }))andmanagement.rewardDeliveries({ cursor })read every page. Both clients fillitemsfromdeliverieswhen an older Relay sends onlydeliveries.useExperience().deliveriesreads the role deliveries again on eachdelivery.updatedactivity event, so a role an operator revoked shows without waiting for focus.defineLeaderboardprints a warning, anddomino checkreports aLEADERBOARD_TIES_DIFFERwarning, when a board's livetiesdiffers from itssettle.ties, including a board that sets onlysettle.ties: "earliest", whose standings share ranks while its prizes pay first-to-reach.leaderboardTiesWarning(board)from@domino-sdk/relay/authoringreturns that message, or null.domino check'sdiagnosticsnow include warnings from data definitions beside its errors; only errors fail it.
Patch Changes
-
a7ede97: The Domino skill references link to the new access page, which lists every kind of credential, the roles and permissions behind it, and what each may do in live, instead of restating the CLI's live limits in the error codes reference. The analytics, on-chain actions and campaign-testing references link to their matching guides. No command or behavior changes.
-
d5fe3db:
domino devprints "Hosted test quests are up to date." when a quest update succeeds after a failed one without publishing anything new, for example after reverting the change that broke the build. Console's Playground reads it to clear the build error its Map tab shows. -
1fa78d2:
limitsreads Relay's platform limits asnumber, so a 1.x release can raise one or add one (released SDKs keep checking the values they shipped with, so Relay answers them within those):limits.quests,limits.questTypes,limits.collections,limits.sdkRuntime,limits.memberCompletions,limits.dashboardPanels,limits.dashboards,limits.defer,limits.retryCooldown,limits.analyticsQueryRows,limits.apps(secrets, KV and SQL stores, migrations, storage browsing and schedules),limits.stagedDeployment,limits.capabilityCalls,limits.trackerData,limits.trackerPreview,limits.walletsPerChain,limits.walletEventLogsandlimits.walletTransactions. It is exported from@domino-sdk/relay/schema,/authoring,/serverand/testing, with itsLimitstype.Deprecated, with unchanged values and types: the literal-typed limit constants
limitsreplaces, which can never change in 1.x. They arequestScaleLimits,sdkRuntimeLimits,maxMemberCompletions,dashboardLimits,deferPolicy,retryCooldownPolicy,analyticsQueryLimit,APP_SECRET_LIMIT,APP_KV_LIMIT,APP_SQL_LIMIT,APP_MIGRATION_LIMIT,APP_MIGRATION_FILE_BYTES,APP_MIGRATION_TOTAL_BYTES,APP_STORAGE_ROW_LIMIT,APP_STORAGE_PREVIEW_CHARACTERS,APP_STORAGE_CELL_CHARACTERS,stagedDeploymentLimits,APP_SCHEDULE_LIMIT,APP_SCHEDULE_MIN_INTERVAL,APP_SCHEDULE_TIMEOUT,APP_SCHEDULE_RESULT_BYTES,maxCapabilityCalls,trackerDataMaxBranches,trackerDataMaxEntries,trackerPreviewLimits,WALLETS_PER_CHAIN,maxWalletEventLogsandmaxWalletTransactions.Deprecated and removed in 2.0.0: Relay's own implementation helpers, which apps and quests have no use for. From
@domino-sdk/relay/server:participantRoutes,participantRoutePattern,sequenceCursorSchema,sourceDataSchema,trackerDataIssue,trackerDataIssues,whereSchemaIssue,validateQuestShape,containsReferralIntegrationReward,REFERRAL_INTEGRATION_REWARD_ERRORandnextScheduleRun. From@domino-sdk/relay/schema:dashboardIssue,normalizeDashboard,compatibleSettingKinds,isBoundSetting,whereSettingsandsourceContent. They keep working until then.zfrom@domino-sdk/relay/authoringstays zod 4 for all of 1.x. -
4842e30: Wording only. The app identity kind example is now
app:game, including the validation message "Name an app identity kind like app:game: …", since a Telegram account is vouched for rather than modelled as an app identity. The JSDoc ofprogress(),quests.claim,me.claimand the quest interactions says which meaning of "progress" and "claim" each uses, anddomino test-members passhelp callsprogress(...)quests counter quests. Examples use the@XDevelopersX account. The changelogs mark the versions that were never published to npm. -
425e4df: Internal: the "HTTPS, or plain HTTP on a loopback host" check for API URLs, sign-in URLs and portal proxy origins now has one implementation, shared through
@domino-sdk/relay/internal. Published behavior is unchanged. -
41f63cb: More CLI commands now call the SDK management client instead of building management routes by hand:
groups,domains,webhooks,schedules,secrets,storage,stock-items,leaderboard-settlements,members identitiesandidentity-links,quests enrollments,quests replay, and every read of the signed-in credential's access. Their responses are checked against the SDK's schemas, and a malformed answer fails withINVALID_RESPONSEinstead of being printed.--jsonoutput is unchanged.A few things behave differently:
domino groups listanddomino groups membersrefuse a--limitoutside 1–100 withINVALID_ARGUMENT(exit 2), as the other paged commands do. Before, any whole number was sent to the API.- An ID, cursor or body that the SDK rejects now fails with
INVALID_ARGUMENT(exit 2) before anything is sent, where the API used to answer 400 or 404. domino webhooks replay,pauseandresumesend a generated action ID when you don't pass--action-id, so a retry after a transient failure is applied once.- Paged reads always send the route's default
limit.
-
e71245d:
createParticipantBackendacceptsonExchangeError(error, request), called with each refused sign-in exchange as Domino answered it (aParticipantExchangeErrorwithstatus,code, Domino'smessageandretryAfter) before the browser gets its answer, so the app's server can log why sign-in failed. The browser's answer is unchanged, also when the callback throws.Behavior change: creating an app login key for one of Domino's own app addresses in the environment that address does not serve is now refused with 400
invalid-request, and the message names the right environment. A<project-slug>--staging.domino.pageor preview address belongs in Test, a live<project-slug>.domino.pageaddress in Live. Other origins, custom domains included, can still use either environment, and existing keys keep working. An origin that is a URL but not an exact HTTPS origin is refused with a message that names the origin it accepts ("Use the exact origin https://app.example.com, without a path, query or trailing slash.") or "Use an HTTPS origin." instead of "Use an exact HTTPS application origin"; over HTTP the management API now answers that message instead of "Invalid management request". The app login guide and the Domino skill references gain a troubleshooting section with the exchange refusal codes. -
ff2e96d: The CLI skill references and the
RELAY.mdthatdomino initwrites now show commands that run as written: the wallet-balance tracker example passes its key, title and start (domino trackers create KEY --title T --source wallet-balances --chain C [--token T] --start MS), the stocked-offer test reads redemptions withdomino redemptions historyinstead of the deprecateddomino redemptions list,RELAY.mdsaysdomino quests listanddomino releases listinstead of the deprecated baredomino questsanddomino releases, and the authoring reference lists every warningdomino checkreports (LEDGER_REFERENCE_DYNAMIC,LEDGER_DECLARATION_MISSING,AUTHORING_IDENTITY_DYNAMICandLEADERBOARD_TIES_DIFFERwere missing). -
8278803: Relay now refuses a malformed personal API key ID with 400
invalid-request("Use the key's ID, such as ak_…") onDELETE /management/v1/tokens/:idandPUT /management/v1/tokens/:id/scope. A key ID isak_followed by letters, digits or_. IDs fromtokens.list()anddomino tokens listalready have that form, so no working call changes: an unknownak_…ID still revokes as a silent success, and a key that belongs to someone else is still refused with 403. -
eb8572b: The testing kit's participant API answers an activity cursor it cannot read as Relay does: 400
invalid-cursor, "Invalid cursor. Start from the first page.", instead ofinvalid-request. -
Updated dependencies [cc04dae]
-
Updated dependencies [1fa78d2]
-
Updated dependencies [4842e30]
-
Updated dependencies [425e4df]
-
Updated dependencies [8515f3f]
-
Updated dependencies [052744c]
-
Updated dependencies [e71245d]
-
Updated dependencies [eaaae2a]
-
Updated dependencies [69e017d]
-
Updated dependencies [bbc3623]
-
Updated dependencies [8278803]
-
Updated dependencies [9cb68e2]
-
Updated dependencies [8ee88a9]
-
Updated dependencies [6fe6f22]
-
Updated dependencies [eb8572b]
-
Updated dependencies [561f4ab]
-
Updated dependencies [8d79fd6]
- @domino-sdk/relay@1.63.0
1.62.0
Minor Changes
-
a341b16: Analytics counts Relay's own
key-usedrejection, of an attempt whose completion key another member's completion holds, as its own rejection source.rejectionsBySourcegainskeyUsed, andanalytics.query'ssourcebreakdown akeyUsedrow;evaluatorno longer includes them, while an evaluator's ownkey-usedstill counts there. EachtopReasonsitem carriescodeSource(relay,evaluator, or null without a code), and Relay'skey-usedand an evaluator's are separate rows, intopReasonsand in thereasonbreakdown. Both fields are optional in the schema, since older servers don't send them.domino analytics getmarks the code of a top reason Relay wrote, such as[relay key-used]or[relay budget-exhausted]. -
5233be5: The experimental billing results read the plan as
BillingPlan(KnownBillingPlan | (string & {})):subscription.planandscheduledChange.planinmanagement.billing.read(),cancel(),resume()andorganization(),billingSubscriptionSchema,projectBillingSchemaandorganizationBillingSchema, and the--jsonresults ofdomino billing show,billing organization,billing cancelandbilling resume, so a plan Relay adds no longer fails to parse. Handle a plan name this SDK does not know. The stablemanagement.usage.plan()anddomino usage planare unchanged: thesubscriptionthey carry keeps the plans this SDK knows (launch,growth,scale). -
3665a4e: Billing is experimental while it is open to Domino staff only: the
management.billingmethods, the billing schemas and types in@domino-sdk/relay/server, and thedomino billingcommands may change in a minor release, plan names included.domino billingnow appears under Experimental commands.management.billing.checkout()andchangePlan()take any plan name (BillingPlan,KnownBillingPlan | (string & {})), anddomino billing checkoutandbilling changetake any--plan; Relay refuses a plan it does not offer with 400, as before.management.usage.plan()anddomino usage planstay stable.The CLI's JSON result schemas now leave objects open, as documented, so a JSON Schema validator such as Ajv accepts fields a later release adds; only the variants of a
oneOfstay closed.webhooks deliveries,storage rowsandstorage keysresults declare the optionalnextCursorthe API already sent.secrets,storageandschedulesresults includeenvironment, and these commands warn on stderr whenRELAY_ENVIRONMENTorrelay.jsonnames live while they use test.contract.jsonmarks deprecated options withdeprecated, as it does commands, andcapabilities.jsonno longer lists deprecated commands.management.referrals.controlandreferralControlSchemanow point topauseandresumeinstead of the deprecatedsuspend. -
f03828d:
domino deploy --only <quest-key>(repeatable) deploys just the named quests, with the SDK runtimes they import, the reference images their code names andsupportedInteractions. Every other quest keeps its current release and every other resource stays as published, so parallel worktrees can publish their own quests without reverting each other's. It works with--previewand--yes; an unknown key, or--onlywith--resume,--promote,--dry-runor--out, fails withINVALID_ARGUMENTbefore any request. -
361a5b1: A deployment keeps the active release of a quest it doesn't change, instead of publishing an equal one with a new ID. Deployment previews and results carry
questChanges: each quest by key,created,updated(with the release it replaces) orunchanged.domino deploylists the created and updated quests in its confirmation and in the--preview --jsonsummary, so a deploy that would replace a quest you didn't touch shows before it publishes. -
68efdac:
domino stagewarns when the project has leaderboards (declared inrelay.jsonor published in test) but its test profile settings are private or name no default account, so every leaderboard row shows a generated name such asParticipant 1a2b3c4d. The warning goes to stderr and, asgenerated-leaderboard-nameswith thedomino profiles configurecommand that shows members' names, into the JSON result'swarnings; the build still stages.domino doctor --remotereports the same as itsleaderboard-namescheck. The Domino skill now says where leaderboard names come from and to ask the owner before showing them. -
90f9465:
domino integrations discord getreads the project's Discord connection, matching thegetname other single reads use.domino integrations discord statusstill works, prints a deprecation warning outside--json, and is removed in 2.0.0.domino collections listlists the project's collection slots in the environment, archived ones included, as{ collections: [{ id, key, title, archivedAt }] }. -
98ad384: A member import carries its real state in a new
statefield:open,checking,checked,applying,applied,stopped,reverting,revertedorcanceled, typed open (KnownMemberImportState | (string & {}), withMemberImportStatelisting the known values), so Relay may add states.statusis unchanged and still readsappliedfor a stopped, reverting or reverted import, so code filtering onstatus === "applied"counts those too; readstateinstead. Import rows gainreverted, true once the import's revert went through the row. Both fields are optional in the schema, since older servers don't send them.domino importanddomino importsresults includestate, and the CLI polls on it.MemberSummary(relay.me(),useMe()) now typesemail,emailsandclockas required, since Relay always sends them: code no longer needs to check them forundefined. The SDK reads a/v1/meresponse from a Relay older than these fields withemail: null,emails: []and aclockat real time (offset 0), the same as code treating them as absent did.memberTime()still accepts an object withoutclock.MemberSummary,LeaderboardPage,LeaderboardHistoryPage,Badge,MemberBadgeandMemberBadgesare marked as response types Relay may add properties to. -
4bf82e5: CLI results and options follow the management API conventions more closely:
- Every command in
contract.jsonhas arecovery:resume,replay,reconcile, ornullfor a command that changes nothing in the API. It says what to do when the command exits 3 withOUTCOME_UNCERTAIN;recoveriesincontract.jsondescribes each value. EveryOUTCOME_UNCERTAINerror that names no saved operation now carriesdetails.recovery: "reconcile", as the automation reference promised; before, onlydomino apiandtokens createset it. offers pause|resume,quests pause|resume,redemptions pause|resume,redemptions cancelandentitlements restoretake--expected-revision N.--revision Nis its deprecated name, removed in 2.0.0, and keeps working; passing both with different numbers exits 2 withINVALID_ARGUMENT.webhooks replaytakes--action-id <uuid>. Relay keeps a receipt for it, so a retry with the same ID answers the delivery the first replay queued instead of queuing another, and the CLI sends it again after a lost answer.tokens list --jsonanswers the keys initemsas well astokens.test-members advance --toalso takes epoch milliseconds.
- Every command in
-
ea2c9be: A retention job can delete the evidence of a quest's attempts decided before a cutoff:
management.attempts.redactEvidenceBefore({ quest, decidedBefore, reason })(reviewpermission; in live it also requires an organization administrator's Console session or personal key, or a service key, and refuses any other caller with403 permission-required, because the deletion can't be undone) deletes up to 100 attempts' evidence per call, oldest decision first, exactly asattempts.redactEvidencedoes for one, and answersmore: truewhile others remain. Attempts still waiting for a decision are skipped, and adecidedBeforein the future is refused with400 invalid-request. Each call records oneevidence-redacted-beforeaudit event.domino attempts redact-evidence-before --quest QUEST --decided-before MS --reason TEXTrepeats the call until nothing is left and prints the total. -
e58c2ae: Name a member's account the way a member import does, everywhere an operator command takes one:
{ kind: "x" | "discord" | "telegram", subject }with the numeric user ID,{ kind: "app:<name>", subject }, or{ kind: "wallet", chain, address }(identityReferenceSchema,IdentityReference).members.identityStates.unlinkandrelinktake it asidentity(memberIdentityStateChangeInputSchema,MemberIdentityStateChangeInput);setSignInalso takes a Cosmos wallet with itsnetwork. An X or Discord subject names the member's selected account with that subject, else the only one they hold. A subject the member holds through several issuers, none of them selected, fails with 409identity-ambiguous; name it byissuerthen.members.lookuptakes it, and sends it as theproviderandsubjectevery Relay version reads.testMembers.linkAccountandunlinkAccounttake{ actionId, kind, subject }for an X or Discord account.- The CLI takes
--kindand--subjectondomino members lookup(with--chainand--addressfor a wallet) and ondomino test-members link-accountandunlink-account.
Deprecated, removed in 2.0.0: the
{ kind: "oauth", provider, issuer, subject }and{ kind: "app", appKind, subject }input selectors,memberIdentityLinkChangeInputSchemaandMemberIdentityLinkChangeInput,members.lookup({ provider, subject }), the test-member{ provider, id }, and the CLI'smembers lookup --providerandtest-members link-account|unlink-account --provider|--id. They keep working through 1.x;contract.jsonmarks the deprecated options withdeprecated.Identity reads gain an optional
source:member,import,app-loginortest-link, an open string (IdentitySource,KnownIdentitySource), andimport, the ID of the member import that attached the identity when Relay recorded it. Identity states of every kind carry them, and so domembers.get'swalletsandidentitiesand the deprecatedoauthAccountsandtelegramAccount.importedis unchanged. -
3a3feef: A leaderboard can rank its live standings first-to-reach.
defineLeaderboard({ ..., ties: "earliest" })orders equal scores by each member's latest qualifying entry, earliest first, then by member ID, and gives every member who scored their own rank: the order a settlement withsettle.ties: "earliest"freezes. Standings pages,me,nearby, placement, and the management and CLI standings reads all follow it, so a tie-broken board is one paged read.tiesis optional. Omitted or"shared", equal scores share a rank as before, so published boards rank unchanged. It is independent ofsettle.ties; set both on a board that settles so members see the order prizes follow. Members listed throughincludeUnscoredstill share one rank after every member who scored. -
10826dc: An organization administrator's CLI session lists the project's identity integrations (app login keys) and their claimed kinds:
domino identity-integrations listanddomino identity-integrations kinds, ormanagement.identityIntegrations.list()andkinds(). They return each key's origin, identity kind and secrets' expiry and last use, never a secret. Creating, rotating and revoking keys and their secrets stays in Console; non-admin CLI sessions and every key type are refused. -
80e09c2: Experimental: operators read a member's marketing consent.
management.members.consent(member)lists the member's current consent, one entry per channel they chose on, andmanagement.members.consentHistory(member, { cursor, limit })pages through every change on every channel, newest first, as{ items, nextCursor }; each entry is{ member, channel, state, at, wording, source }, andmemberConsentHistorySchemaandmemberConsentHistoryQuerySchemaare exported from@domino-sdk/relay/schema. Both needreadpermission (GET /management/v1/members/:member/consentand/consent/history); a member who never chose reads as empty, and an unknown member fails with 404not-found.domino members consent MEMBERprints both, with--limitand--cursorfor the history. The Console member page shows the same, read-only. There is no management write: only the member grants or withdraws consent. The consent API is@experimentaland can change in a minor release. -
1c61073:
domino tokens list|create|scope|revokemanage your personal API keys from adomino loginsession, asmanagement.tokensdoes.domino tokens create --name NAME --permission P [--ingest-tracker T] [--days N]issues a key for the selected project and environment and prints its one-timetokenand expiry;--jsonprints the SDK's answer, sodomino tokens create … --json | jq -r .token | domino secrets set NAMEstores it. A personal key expires afterdays, 1 to 90, default 30;--days 91is refused before anything is sent.domino tokens listshows each key's expiry. API keys still cannot manage keys (credential-not-allowed), and a credential that must not expire is a service key, which an organization admin creates in Console. -
423b6fc: Plugin connections, experimental.
management.connections(list,get,connect,update,disconnect) connects a project to the plugins Domino writes, separately in test and live; a promotion never copies a connection. A connection's config is checked against the plugin's JSON Schema (422invalid-confignames the failingpath), its secrets are write-only and sealed at rest, every change takes anactionId, andupdateanddisconnecttake anexpectedRevision(409connection-changed). The new exports (connectionSchema,connectionPageSchema,Connection,ConnectionStatusand the connect, update and disconnect inputs) are experimental and may change in a minor release. The CLI adds the experimentaldomino connections list|get|connect|update|disconnect, test unless--environment live, reading secrets from stdin as JSON or at hidden prompts, never from arguments. -
2a83792: Plugin OAuth connections, experimental. A plugin whose
plugins.list()entry hasoauthconnects through its vendor's OAuth instead of entered secrets:management.connections.authorize(plugin, { config, returnTo?, actionId? })answers{ authorizationUrl, expiresAt }, a person opens the URL and grants access, and the vendor's callback makes the connectionready. The URL works once, for ten minutes. Domino refreshes the tokens before they expire and never answers them; when the vendor refuses a refresh, the connection isattentionwith the newreconnectReason(revokedorexpired), and authorizing again reconnects it. Disconnecting deletes the tokens, then revokes them at the vendor; a failed revocation is logged.connectrefuses an OAuth plugin with 409oauth-required,authorizerefuses one connected with secrets with 409oauth-not-supported, and a deployment without the vendor's OAuth app with 409plugin-not-configured(oauth.configuredis false). New experimental exports:connectionAuthorizeInputSchema,connectionAuthorizationSchema,ConnectionAuthorizeInput,ConnectionAuthorization,ConnectionReconnectReason,KnownConnectionReconnectReasonandknownConnectionReconnectReasons. The CLI adds the experimentaldomino connections authorize <plugin> --config-json …, which prints the authorization URL. -
d64625c:
domino quests list --lifecycle <state>prints only the quests in that lifecycle state:active,pausedorarchived. Repeat the flag for more than one state. Any other value exits 2 withINVALID_ARGUMENTbefore a request is sent. Without the flag, the list is unchanged; each quest in it carrieslifecycle. -
b2b8127: A full deployment to test warns
quests-not-in-bundlewith the keys of quests published from code that it leaves out, such as a starter's sample quests whose files were deleted: they stay as published until archived withdomino quests archive <key> --reason "…". Archived quests and quests created in Console are not listed. A deployment input withpartial: truedoesn't warn, anddomino deploy --onlysends it.domino stagenow prints the warnings of the deployment its build published, and its--jsonresult carries them inwarnings; a build's status (GET /builds/{id}) names that deployment indeployment. -
a381dde: Staff can delete the evidence a member submitted for a decided attempt:
management.attempts.redactEvidence({ attempt, reason })anddomino attempts redact-evidence ATTEMPT --reason TEXT(reviewpermission, test and live). The photo, text, URL or quiz answers, and what the quest's capabilities read from them, are deleted; the attempt keeps its outcome, completion and rewards. Attempts that submitted the same photo are redacted together.management.attempts.getanswers the newredaction({ at, actor, reason }). Undecided attempts are refused withattempt-not-decided, andattempts.retryrefuses a redacted attempt withevidence-redacted. -
c8452bc: Add
domino starters export <template> --directory <dir>, which writes the starter thatdomino createuses today, including.domino/starter.json, into a new or empty directory so an existing hosted project can move to a newer starter. -
7fa2a06: Test members can submit photo quests.
management.testMembers.uploadEvidence(member, file, { actionId })uploads a JPEG or PNG of at most 5 MB as the test member's photo, andactsubmits itsidasevidence.domino test-members act MEMBER QUEST --photo FILEdoes both, so the quest's real image check and the review queue can be proven from the CLI. The campaign-testing reference now points photo evaluator tests atfacts.images.review. -
4391bad: Every
domino test-memberschange takes--action-id ID: any action ID the management client accepts, sent as the change'sactionIdwithout saving an operation, so the CLI and the management client can retry the same action.
Patch Changes
-
8439ff6: The Domino skill references and
capabilities.jsonnow match shipped behavior. Console shows dashboards on Analytics. Webhooks list all nine event types. A newquest-announcementscapability covers quest completion announcements. The trackers reference listspause,resume,refreshandtriggerOutcomes, says the plan's minimum refresh interval applies to every collected source, and explains that a spent quest budget rejects the attempt instead of skipping the observation. Deprecated methods are no longer recommended: useuseRedeemOffer().mutateandmembers.identityStates.relink. -
ee3f488: Bulk management writes (
catalog.deploy, catalog previews and drafts,deployments.preview,releases.publishBatch, tracker record ingest andmemberImports.addRows) refuse a body with more than 2,000,000 JSON values with 413payload-too-largebefore parsing it, whatever its byte size; other management writes refuse one past 500,000. A server handles one body over 4 MiB or 500,000 values at a time and answers another 503 withRetry-Afterwhen it has waited 10 seconds.memberImports.addRowsnow takes at most 64,000,000 bytes, like the other bulk writes, so a single batch above 64 MB from an older CLI gets 413.domino importnow splits rows into batches of at most 500 rows, 16 MB and 1,000,000 JSON values, sends a row larger than that in a batch of its own, and refuses a row too large for one call (63 MB or 1,990,000 values) before uploading anything, naming its line.The management client, the CLI and the participant client now send any call again after a 503 with
Retry-After, within its deadline and attempts, including writes without an action ID: Relay answers such a 503 only to a request it refused before doing anything. Before, those writes were sent once. -
f13cfb6: A
domino loginCLI session creates and scopes personal API keys in test only.management.tokens.createwithenvironment: "live", andmanagement.tokens.scopeon a live key, now answer 403console-approval-requiredfrom a CLI session, whateverX-Relay-Environmentselects: a live key would carry the live operations (points, attempt confirmations, reviews, pauses) that the CLI session itself is refused, and would outlivedomino logout. Create and change live API keys in Console (Settings → API keys). A CLI session still lists its keys and revokes any of them, live included, and Console sessions are unchanged.This changes a 1.x behaviour, an exception Peter Szoke granted on 2026-10-15: no request or response shape changes, and the refusal is a typed 403 that says where to go.
domino tokens create --environment liveprints that refusal, anddomino tokens --helpsays live keys come from Console. -
c391c86:
domino deploy --promote <id> --environment liveruns in the checkout's scope, fromrelay.jsonlikedomino promote, instead of failing withSCOPE_REQUIRED. It starts a new operation rather than resuming one; a promotion whose response was lost is still recovered withdomino operations resume <id>, in the scope it journaled.domino deploy --resumeanddomino stage --request-idstill require every scope key as a flag orRELAY_*variable. -
154af63: Discord message and reaction records name a thread's parent channel:
DiscordMessageDataandDiscordReactionDatagainparentChannelId, the channel a thread belongs to (a forum post's forum), ornulloutside a thread. It is absent when it is unknown, because Discord's channel read failed.channelIdstays the thread's own ID. A trigger'swhere: { path: "parentChannelId", equals }selects a channel's threads; to count a channel and its threads, compare both fields in the evaluator.management.testMembers.recordanddomino test-members record --datatakeparentChannelIdfor Discord records. -
550b610:
domino doctor --remote'sparticipant-apicheck, itsscope.participantApi, anddomino devread theapiUrlwritten in relay.json, not the management URL a checkout scope (domino checkout --api-url) keeps in the Git directory. A checkout made with--api-url https://console.domino.run/relayand a relay.json pointing athttps://relay.domino.runis now healthy instead of reporting relay.json as wrong. -
b83512e:
domino deployreports a timeout or lost connection while preparing its preview asTIMEOUTorNETWORK_ERROR(exit 1) and says nothing was published, instead ofOUTCOME_UNCERTAINwith no operation to resume. -
5e635dc: The CLI requires Node 24.5.0 or later (
engines.nodeis>=24.5.0 <25), because its first request loads system certificate authorities withtls.setDefaultCACertificates, which Node 24.5.0 added. On an earlier Node 24 releasedomino loginand every other command now exit 1 withDomino CLI needs Node 24.5.0 or newerinstead of failing the first request withCould not reach the API: setDefaultCACertificates is not a function. TheNODE_VERSION_UNSUPPORTEDerror addsdetails.minimumVersion(such as"24.5.0");details.minimumstays the major version. -
986f989:
domino loginsays when its code expires, and the--jsonbrowser-loginevent carriesexpiresAt(epoch milliseconds). A sign-in request now lasts 60 minutes instead of 10, so the person approving it can come back to the link later. -
9ebd5dc:
domino createwaits for a slow repository setup instead of stopping at the 30-second request deadline. When a step times out, loses its connection, or gets a 502, 503 or 504, it resumes the same saved operation for up to 5 minutes and then prints the normal result. Without--jsonit says once on stderr that it is still setting up the project. Past 5 minutes it fails as before, with the operation to resume and a message in plain words: Domino may still be setting up the project, or the API stayed unavailable. -
f204520:
domino stageends its human output with who can open the staging URL: staging is private and runs on test data, reviewers outside Console open it with a staging password (Console Settings → Staging access, ordomino api PUT /staging/access), and members see the app only once a promotion makes it live.--jsonoutput is unchanged. The Domino skill's hosted reference shows thePUT /staging/accessbody and tells agents to ask the owner before setting or changing the password. -
44a083c: The Discord capability tells agents that a Console administrator without Manage Server can send a one-time install link (Send to your server admin, valid seven days) to whoever manages the server, who needs no Domino account.
-
acad848:
domino doctoroutside a project reports setup health only: no relay.json is amanifestwarning instead of an error, the skill loader check and theagentsfield are left out, and a machine with nothing else wrong reportshealthy: trueand exits 0. Inside a project nothing changes. Human output labels the loader field "Skill loaders", since it lists the Domino skill loaders installed in the project, not which agent is running doctor. -
8b95b72: The Domino skill's
trackersreference now covers declaring trackers: maintained X datasets, rewarding X posts, tracker triggers, content admission and performance gates, daily engagement points, and the Discord, Telegram, YouTube Shorts, TikTok and wallet balance sources. They moved there from theauthoringreference, which links to them, sodomino reference trackersserves them and an agent writing a quest no longer loads them. -
2112bd9: The Domino skill has a new
verificationreference for checking what a member did: the evaluator reader rules (null results, errors and pinning), declared integrations, Discord membership, X, linked wallets, wallet balances, NFT holdings, contract state, Cosmos delegations, on-chain actions, text and URL submissions, AI text review, image checks, staff confirmation, and entry after app-owned pass verification. These sections moved there unchanged from theauthoringreference, which links to them, sodomino reference verificationserves them and an agent writing a quest loads them only when it needs them. -
a06d381: The Domino skill has a new
quest-rulesreference for how a quest decides and repeats: campaigns, windows and prerequisites, automatic and repeatable quests and republishing them, prerequisite automatic outcomes and test members, reject codes, deferred evaluation and progress reports, distinct-action progress counters, retry and completion cooldowns, evaluation time and randomness, calendar-day streaks, and testing quests locally. These sections moved there unchanged from theauthoringreference, which links to them, sodomino reference quest-rulesserves them and an agent loads them only when it shapes quest behavior. -
d820a34: The Domino skill has a new
managementreference for operating published resources: the management client and its error contract, project identity and settings, resource lifecycle, canonical lifecycle commands, recovering a quest lifecycle change and its keys, operator undo commands, and scheduled hosted jobs. These sections moved there unchanged from theauthoringreference, which now covers writing and publishing quests, rewards and presentation and links to the trackers, verification, quest rules and management references, sodomino reference managementserves them and an agent loads them only when it operates a project. -
56a0601:
fakeEvaluationMember(methods)from@domino-sdk/relay/testingbuilds anEvaluationMemberfor a test that assembles its own evaluation context. Pass the methods your code reads; the rest answer for a member with no linked accounts, completions, groups or enrollments, andbalanceandreferralsfail with aCapabilityErrorof codeauthor-error. A member written by hand stops compiling when a minor release adds a method; one fromfakeEvaluationMemberdoes not.The CLI's automation contract (
cli/contract/contract.json) lists the JSON Schemas ofrelay.jsonand the checkout'sdomino.jsonunderfiles, so a later release is checked to accept every file an older CLI wrote. -
5cc0d4d: Revoking a completion with
freeOccurrence: truenow returns the budget it used. Each quest budget the completion was charged gets that charge back, in the same transaction and in the budget period it was charged to, so the freed occurrence can be completed again within the cap. A point-priced budget gets back its charge less the points the revoke couldn't reverse.CompletionRevocationreports this in a new optionalbudgets(budget,released). Revoking without freeing the occurrence leaves budgets spent, as before. Completions recorded before this release give no budget back and reportbudgets: [].Restoring such a revocation charges that budget again and reports it in a new optional
CompletionRestoration.budgets(budget,charged). The charge is checked against the budget's current limit, or the limit it was first charged under if the quest removed the budget or changed its period or cost. Restore now also refuses with 409budget-exhausted, naming thebudget, when a budget has no room left for the charge, and then changes nothing. This is a behaviour change: until now, budgets stayed spent after any revoke. -
dcc32da:
ctx.wallet.events()takesnewest, from 1 to 1,000: it returns at most that many of the newest matching events, reading back from the newest block, and stops at the 3,000,000-block or 1,000-log cap instead of throwingauthor-error.WalletEventsgains an optionalcomplete,falsewhen anewestread left older matching events out or stopped beforesince. A busy wallet, or a long window on a fast chain such as Arbitrum One (3,000,000 blocks is about 9 days there), no longer has to fail the attempt. A call withoutnewestbehaves as before: it returns every matching event, now withcomplete: true, or throwsauthor-error. A read whose start-block estimate overshot 3,000,000 blocks while the span itself fit is no longer refused. Theonchain-actionsnippet reads withnewest: maxWalletEventLogs. -
980e6ca: Sign-in rate limits count an IPv6 client by its /64, so every address in one /64 shares the per-IP limits on email, wallet, OAuth and Telegram sign-in starts,
domino logindevice starts and staging password attempts. An IPv4-mapped IPv6 address counts as its IPv4 address. Per-address limits now count per client: three sign-in codes per email address and ten wallet challenges per wallet address from one IP address every ten minutes, so another client hitting them no longer refuses that address's own sign-in, on this project or another. Each project and environment also caps an address at ten sign-in codes and thirty wallet challenges every ten minutes from all clients together. -
d683d00: Sign-in rate limits per IP address count each member of a Domino-hosted app on their own. Hosted apps reach the API from Cloudflare's shared Worker address, so until now every member of every hosted app shared one set of per-IP limits. Domino now attests the visitor's IP on every request a hosted app's server sends to the API, with no app change or rebuild. A request from Cloudflare's shared Worker address without Domino's attestation, such as from an app you run on your own Cloudflare Workers, is counted per project and environment instead of with every other Worker.
-
081806c: Personal keys (
ak_…) and MCP OAuth tokens that Clerk does not recognize now count against the client that presented them, on the management API,gitand the MCP server: after 30 in 10 minutes from one IPv4 address or IPv6 /64, counted by each API server on its own, everyak_and MCP token from it answers 429rate-limitedwithRetry-Afteruntil the window ends. Expired, revoked and other refused credentials Clerk knows are not counted, and neither is Clerk refusing Domino's own secret key. A refused value presented again within a minute can be refused without asking Clerk, and a value no Clerk credential can be (under 16 characters after its prefix, over 4096, or with a character a bearer token cannot hold) is refused with 401invalid-credentialwithout asking Clerk. When Domino is checking many new credentials at once, a key or token it has not accepted recently can answer 503 withRetry-After: 1. Clerk's own rate limit now answers 503 with Clerk'sRetry-After. -
114e0c7: Telegram and Discord install links now tell whoever opens them which Domino project they connect to. The Telegram bot's reply to a link, before the chat picker, names the organization, the project's name and slug, and the environment, and so does its
Connected …reply. A Discord install link sent to a server's administrator (Send to your server admin) first opens a page naming the same, with a Continue to Discord link; the page changes nothing, so a link preview that fetches it stays harmless, and the page shown after connecting names the project too. Creating either link (management.telegram.connect(),POST /management/v1/integrations/discord/links) now reads the organization's name from Clerk, and answers 503 rather than issue a link that names nobody when Clerk can't answer. Links made before this change still work and name no project. Console's own Discord installation is unchanged. -
92ee725: Connecting a Discord server or Telegram chat where Domino's shared bot already holds rights now takes someone who holds those rights too. In Discord, installing the bot (Console's Connect Discord or a shared install link) refuses a user who is neither the server's owner nor an Administrator when the bot already has a permission there they lack, such as Manage Roles another organization gave it; the install page says "Domino's bot already has permissions in this server that you don't have. Ask a server administrator to connect it." and nothing is connected. A fresh install, where the bot holds nothing beyond
@everyone, still needs only Manage Server. In Telegram, an administrator who lacks an administrator right the bot holds in the chat, such as posting messages, is refused with a reply naming the missing rights; the chat's creator always connects it. Reusing a Discord server the organization already connected is unchanged, and existing connections are not revisited. -
d7df1d9: The automation skill reference documents management API rate limits: no general quota per key, project or organization, the calls that carry their own limits, and how to honor 429 or 503
Retry-Afterand pace a long run. -
dd7ced3: Experimental: a member import row can carry the member's marketing consent from the platform they come from. The optional
consentlist onmemberImportRowSchematakes one{ channel, state: "granted" | "withdrawn", at, wording? }per channel; applying the row records it with its original time and wording, markedsource: "imported", in the row's transaction, unless the member chose on that channel in Relay (their choice always stays) or an earlier import recorded consent as recent or more recent. A check refuses the row withfuture-actionfor a time in the future,unsupported-consent-channelfor a channel other thanemail,consent-wording-required,consent-wording-too-longorconsent-wording-invalidfor the wording, andduplicate-entryfor a channel named twice.KnownConsentSourcegainsimported. The CLI imports reference describes the field. -
5a34b36: Experimental: members record their marketing consent per channel.
relay.consent.get()lists the signed-in member's current consent, one entry per channel they chose on (member,channel,state,at,wording,source), andrelay.consent.set({ channel, granted, wording, actionId? })grants or withdraws one channel. TheuseConsent()React hook returns the query withgrant(channel, wording)andwithdraw(channel, wording?). Relay stamps the time and stores the wording verbatim; a repeat of the current state and wording records nothing. Onlyemailis accepted for now: other channels are refused withunsupported-consent-channel, a grant without wording withconsent-wording-required, wording over 2,000 characters withconsent-wording-too-long, and wording with a NUL or a lone surrogate withconsent-wording-invalid. Channel, state and source are open strings in results. The consent API is@experimentaland can change in a minor release. The participant API reference andcapabilities.jsondescribe it. -
f463bdf: The campaign-testing skill reference explains how to pace a live test-member rehearsal: a driver process outside the hosted app, a deterministic
actionIdor--operation-idper step, bounded concurrency, and clock moves instead of real-time waits. -
172c3b5: Experimental: a
viewerproject role and grant end dates, to share a project's dashboards with a sponsor or client. A viewer holds one permission,dashboards, which readsdashboards.listanddashboards.readand nothing else: no members, evidence, quests or settings. Every other role holds it too, andreadkeeps reading dashboards, so no existing person or key loses access.management.grants.settakesprojectGrantSchema:rolesmay nameviewer, and an optionalexpiresAt(epoch ms, or null for no end) ends the grant's access without anyone removing it. An ended grant stays ingrants.list(), and the access history records each end date. Released SDKs keep reading grants: a grant'sroleskeeps the roles 1.x names, so a viewer's is empty, and the newprojectRoleslists every role. Likewise a project inmanagement.access()keepsdashboardsout ofpermissionsand lists it in the newprojectPermissions.projectRoleSchema,projectPermissionSchema,projectRolePermissions,projectGrantSchemaand these fields are experimental.Console grants the viewer role and an end date under Settings → Access → Team, and shows an ended grant as Expired. A viewer's Console shows only Analytics, opened on the project's first dashboard. The CLI's agent references describe the role.
-
aaf28f9: Quest submissions are limited to 120 an hour per member in each project environment, counted together across
POST /v1/me/attempts,POST /v1/attempts,POST /v1/me/submissions/:id/submitandPOST /v1/attempts/:id/retry. Past it they answer 429rate-limitedwithRetry-After, before the request is read. The quest controllers keep the saved draft, soresume()continues it. The participant reference in the Domino skill states the limit. -
9f39ea4: An operator's reversal of a referral payout stays reversed. Reversing all or part of a referral payout (Console point history Reverse, or
management.points.record({ kind: "reverse", entry })) writes those points off on the referral account, so no later reconcile pays them again; earlier, the inviter's next invitee activity or a new acceptance of their code paid the reversed amount back. Later bonus growth still pays, and restoring a revoked qualification does not repay the reversed points.earnedin the referral summary and the management referral accounts excludes the reversed points, so a reversed payout no longer reads as earned. -
10df298: When no credential exists for the selected API URL but
domino loginsaved one for another,AUTH_REQUIREDnames that URL and the fix (--api-url, a checkout that uses it, ordomino login), anddetails.savedApiUrlslists the saved URLs so automation can retry. A saved token is still sent only to the API URL it was saved for. With no other saved sign-ins, the error is unchanged. -
1cdc64b: Staging a commit the project's hosted repository doesn't have (a mistyped SHA, or a commit not pushed yet) now answers a certain 422
commit-not-foundbefore any build is reserved, instead of an uncertain 502upstream-failed.domino stagereports it asREJECTEDwithapiCode: "commit-not-found", saves no operation to resume and prints no retry command: push the commit, then stage again. Other GitHub failures during the commit check still answer 502 and stay uncertain. -
3d2f654: Staging's participant API is now private, like staging itself. A participant request that names a staging address (
<slug>--staging.domino.page) inX-Relay-App-Originanswers only when it carries the staging pass, which the staging gateway gives browsers signed in to staging; anything else is refused with 403staging-access-required. Apps on staging keep working with no change: the gateway adds the pass to the requests it hands the app and to legacy/relay/*calls, andproxyParticipantforwards it with the browser's other cookies. Only callers that sent a staging origin straight to the API, outside the staging address, see the new refusal. The retiredstaging-<slug>.domino.runorigin now selects nothing (403 "Application origin is not registered or is offline"). Previews, live apps and identity integrations are unchanged. -
2e8f383: The Domino skill's campaign testing reference explains the staging toolbar's bottom inset: while the toolbar is mounted,
<html>carriesdata-domino-staging-toolbarand--domino-staging-toolbar-inset-bottom, so an app's fixed bottom bar can sit above the collapsed toolbar instead of under it. When Playwright reports "<domino-staging-toolbar>intercepts pointer events", the app should use the inset. -
75efef0: The skill's references list the participant reads that return a member's submitted text, URL or quiz input, and warn against text or URL quests for secrets. The trackers reference shows the pattern to use instead: the app's server checks the code, rate-limits guesses, and ingests a record for the member into a custom tracker that triggers the quest.
-
b9270ff: Telegram Login, the member's Telegram account and its unlink now go through the generic sign-in surfaces, and Telegram's own members for them are deprecated. They keep working, with their routes, through SDK 1.x and are removed in 2.0.0:
relay.auth.telegram.start(mode)→relay.auth.start({ provider: "telegram", mode }).relay.auth.telegram.account()→ the"telegram"entry ofrelay.auth.signInAccounts().relay.auth.telegram.disconnect()→relay.auth.disconnect("telegram").useTelegramAccount()→useAccount("telegram"). Inside a Mini App, link withrelay.auth.telegram.miniApp.connect(initData), then callinvalidate([relayKeys.accounts, relayKeys.experience])fromuseInvalidate().useSignIn().withTelegram()→useSignIn().withProvider("telegram");withProvidernow takes anySignInProvider.SignInOptions.telegramanduseSignInOptions().telegram,TelegramSignInOptionandSignInActions.telegram→signInProviders, below.relay.auth.providers().telegram.loginandmanagement.participantSignIn().telegram.login→ the"telegram"entry ofsignInProviders.TelegramSignInMethodsand the newParticipantSignInTelegramare written-out types, so editors show these two deprecations.management.members.telegramAccount(member)andunlinkTelegramAccount(member, input)→members.accounts(member)andmembers.unlinkAccount(member, { provider: "telegram", subject, expectedRevision, reason }).
useSignInOptions()andsignInOptions(relay)addsignInProviders, the open sibling of theoauthentries ofoptions: every sign-in provider Domino configured, Telegram included, in Relay's order, each aProviderSignInOption({ kind: "provider", id, label, icon, start() }). A newer Relay may list a provider this SDK does not know; its label is its name. Against a Relay withoutsignInProviders, the list holds its providers and Telegram Login.SignInMethodsaddssignInProvidersandSignInActionsan optionalprovider(provider)action. The starters render their provider buttons fromsignInProviders.Mini App sign-in (
relay.auth.telegram.miniApp,useSignIn().withTelegramMiniApp,providers().telegram.miniApp,participantSignIn().telegram.miniAppBotId) is not deprecated. -
90a20c6: Telegram is now a plugin:
management.connections.list()anddomino connections get telegraminclude it,readywhile the environment has a connected chat,attentionwith the chat's issue while Domino's bot can't read one, anddisconnectedwithout chats. Chats still connect only through the bot (management.telegram.connect(), unchanged), soconnections.connect,updateanddisconnectanswer 409plugin-managedfor Telegram and for any plugin that connects through its own flow.knownPluginSlotsaddsannouncement, the slot Telegram posts quest announcements through.management.plugins.list()items gainconsole:{ component }when Console shows the plugin's own component instead of the generated card, otherwise null; the schema defaults it to null, since older servers don't send it. -
8e8e24a: Telegram Mini App sign-in has its own entrypoint,
@domino-sdk/relay/telegram(experimental):withTelegram(relay)addsrelay.telegramto a client and answers the same client,relay.telegram.miniApp.signIn(initData)and.connect(initData)do whatrelay.auth.telegram.miniAppdid, andtelegramMiniAppInitData()reads the launch data. Apps that don't import it don't carry it.relay.auth.telegram.miniAppand thetelegramMiniAppInitDataexports of@domino-sdk/relay/browserand@domino-sdk/relay/reactare deprecated, keep working through 1.x and are removed in 2.0.0.Telegram Login and Mini App sign-in now run in the Telegram plugin, with no change to what they accept or answer: members linked before keep signing in as the same member, and
/v1/auth/telegram/mini-apptakes the same requests.management.plugins.list()anddomino connections get telegramlist one more slot for Telegram, whichknownPluginSlotsadds:identity(members sign in with it). -
52ed037: Telegram's trackers and
ctx.telegramnow come from the Telegram plugin, with no change to what they record or read:telegram.messages()andtelegram.reactions()trackers, the bot's webhook at/v1/integrations/telegram/updates, andctx.telegram.connection()andmember()behave as before, and published quests keep working.management.plugins.list()anddomino connections get telegramlist two more slots for Telegram, whichknownPluginSlotsadds:tracker-source(the plugin writes its trackers' records) andevaluator-capability(quest code reads it asctx.<plugin>). A quest may now declare thetelegramintegration module,{ provider: "telegram" }, and publishes; before, publishing refused it withunknown-integration-module. Declaring it is optional, since every quest getsctx.telegram. -
Updated dependencies [a341b16]
-
Updated dependencies [bd622ae]
-
Updated dependencies [76be180]
-
Updated dependencies [5233be5]
-
Updated dependencies [ee3f488]
-
Updated dependencies [3665a4e]
-
Updated dependencies [f13cfb6]
-
Updated dependencies [361a5b1]
-
Updated dependencies [154af63]
-
Updated dependencies [79b7c68]
-
Updated dependencies [eed1808]
-
Updated dependencies [56a0601]
-
Updated dependencies [5cc0d4d]
-
Updated dependencies [94ca2b5]
-
Updated dependencies [f9c9f34]
-
Updated dependencies [dcc32da]
-
Updated dependencies [98ad384]
-
Updated dependencies [4bf82e5]
-
Updated dependencies [4bf82e5]
-
Updated dependencies [4bf82e5]
-
Updated dependencies [d4fcade]
-
Updated dependencies [8064983]
-
Updated dependencies [980e6ca]
-
Updated dependencies [d683d00]
-
Updated dependencies [081806c]
-
Updated dependencies [114e0c7]
-
Updated dependencies [92ee725]
-
Updated dependencies [ea2c9be]
-
Updated dependencies [2de5d71]
-
Updated dependencies [e58c2ae]
-
Updated dependencies [3a3feef]
-
Updated dependencies [10826dc]
-
Updated dependencies [c391c86]
-
Updated dependencies [7a93f76]
-
Updated dependencies [1a70098]
-
Updated dependencies [dd7ced3]
-
Updated dependencies [5a34b36]
-
Updated dependencies [4b7c062]
-
Updated dependencies [de99154]
-
Updated dependencies [80e09c2]
-
Updated dependencies [1c61073]
-
Updated dependencies [423b6fc]
-
Updated dependencies [01b789e]
-
Updated dependencies [2a83792]
-
Updated dependencies [172c3b5]
-
Updated dependencies [aaf28f9]
-
Updated dependencies [b2b8127]
-
Updated dependencies [a381dde]
-
Updated dependencies [9f39ea4]
-
Updated dependencies [b866593]
-
Updated dependencies [1cdc64b]
-
Updated dependencies [3d2f654]
-
Updated dependencies [4443124]
-
Updated dependencies [b9270ff]
-
Updated dependencies [90a20c6]
-
Updated dependencies [8e8e24a]
-
Updated dependencies [52ed037]
-
Updated dependencies [2fe2d72]
-
Updated dependencies [7fa2a06]
-
Updated dependencies [06046f1]
- @domino-sdk/relay@1.62.0
1.61.0
Minor Changes
-
e71f881:
domino checkruns each badge, point ledger, offer, leaderboard, referral, dashboard and tracker entry in a separate Node process and parses its export with the schema Relay parses it with at deployment. A definition that fails, such as an offer withoutstock, reports aDEFINITION_INVALIDerror diagnostic with itsfile,export, fieldpathand message, and fails the check instead of failing later in staging.defineOffer,defineBadge,defineLedger,defineLeaderboard,defineReferral,defineDashboardanddefineTrackerstill throw aZodErrorfor invalid input, and its message now names the kind, key and fields, for exampleOffer "ladder" is invalid: stock: Invalid input: expected number, received undefined. Staging shows that message when its data setup fails. A deployment, preview or staging activation with an inline or described definition that fails its schema now answers 422validation-failedwith that message, where it answered 400Invalid request. -
b369a75: A project connects a Discord server that another project of its organization already connects, ready, in the same environment, without installing Domino's bot again. Any credential with
publishpermission can: an automation key, a CLI session, a personal API key or Console (Integrations, Or reuse). Installing the bot into a new server stays with an organization administrator in Console.management.discord.servers()anddomino integrations discord serverslist the servers the project can connect.management.discord.connect(server)anddomino integrations discord connect --server <id>connect one, once the bot still sees it. They refuse with 409server-not-connected-in-organizationfor any other server, 409integration-connectedwhen the project already has a Discord connection, and 422server-not-reachablewhen the bot lost the server. Connecting the server the project already connects changes nothing.management.discord.connection()anddomino integrations discord statusread the project's Discord connection.
The project's audit records each connection as
discord-server-connected, with who connected it and the project whose connection it reused. An automation key and a CLI session connect only in test. -
d657954:
domino stagewarns when the app signs participants in with a wallet but the test environment has no wallet family enabled, which leaves the app's wallet sign-in doing nothing. The warning goes to stderr and, aswallet-sign-in-offwith the enablingdomino api PUT /participant-sign-incommand, into the JSON result'swarnings; the build still stages.domino doctor --remotereports the same as itswallet-sign-incheck. The CLI reads wallet sign-in from each of the app's source files:relay.auth.wallet.signIn,useSignIn().withWallet,useSignInWallets, Cosmos chains passed touseSignInOptions, a wallet option picked withfind, or wallet options filtered without OAuth, email or Telegram ones. The starters' sign-in, which renders every option the project offers, does not count.
Patch Changes
- 97b71e2: The
dominoskill's error reference no longer listsorganization-not-provisioned: Relay serves every organization, so no request is refused with it. - Updated dependencies [e71f881]
- Updated dependencies [f0ce453]
- Updated dependencies [b369a75]
- @domino-sdk/relay@1.61.0
1.60.0
Minor Changes
-
d258795: A budget can cap what one person gives, not only what one member receives.
budget({ key, scope: "author", period, limit, cost })counts per tracker record author: the account that caused the record, such as a reaction's reactor, whether or not a member linked it. Put it in a tracker quest'sbudgetsbeside a"member"budget to cap both sides, for example a tip paid to a message's author withbeneficiary: "messageAuthor": at most 20 tips a day per receiver and 10 a day per reactor. A tip over either cap is rejected withbudget-exhausted, permanently; other reactors' tips keep paying. It works ontelegram.reactions(),discord.reactions(), kudos recipients and XinReplyToreplies. Author budgets share keys, calendar periods and the atomic commit with the other budgets, and imported completions don't count toward them.An author budget needs a tracker trigger.
domino checkreportsAUTHOR_BUDGET_TRACKER_REQUIREDon another quest,domino buildanddomino deployrefuse it, and Relay refuses its publication.The types SDK 1.x code reads are unchanged:
Budgetkeepsscope: "member" | "project", and a quest's description lists author budgets in the new optionalauthorBudgets(AuthorBudget,authorBudgetSchemafrom@domino-sdk/relay/schema), apart frombudgets.budget()returns anAuthorBudgetforscope: "author". A member's questbudgets(QuestBudgetView) leave author budgets out, since they cap someone else's giving. -
c619bf3: New
domino deploy rollback [deployment]publishes the previous published test deployment again, undoing the latestdomino deploy. It names both deployments and asks before publishing (--yesskips that), and it saves the change under an operation ID, sodomino operations resumefinishes it once after an uncertain response. Only the previous published deployment can be rolled back to. Only what it contains goes back, and the staged build does not change; once a hosted app build has published to test since that deployment, the rollback is refused with 409conflict. In live it fails withENVIRONMENT_UNSUPPORTEDandapiCode: "promotion-required".management.deployments.rollbacknow works with a test client too, publishing at once; in live it still submits the rollback for review. -
e899f47: Member imports carry referrals. A row's optional
referralnames the member's inviter (another row'sref, or the sign-in identity of an existing member), when they accepted, and the referral programs they already qualified in (qualified: [{ program, at }]). Applying records the referral and the qualifications without paying any referral reward, milestone or bonus; they count toward the inviter's qualified friends, and the friend's later points pay the program's bonus. An inviter's row applies before the rows that name it. New refusal codes:unknown-inviter,self-referral,inviter-already-accepted,referral-cycle,inviter-refusedandunknown-referral-program. The check summary addsreferrals(accepted, andqualifiedby program),management.referrals.participants()names the import that recorded a relationship or qualification inimport, andmanagement.members.get()returns the member's accepted referral in an optionalreferral(inviter,acceptedAt,suspended,import). -
8ee2000: A member import vouches for Telegram and Discord accounts too. A row's
accountstakes{ kind: "telegram", subject }and{ kind: "discord", subject }, each a numeric user ID, at most one of each per member, fromdomino importandmanagement.memberImports.addRows. They work like a vouched X account: Telegram and Discord tracker records, quest checks and Discord role checks trust them from when the import attached them, nobody else can link them, and the member's own Telegram or Discord link replaces them. They never sign in: a Telegram or Discord sign-in with one is refused withsign-in-method-disabled. A row naming a second account of a kind, or another one than the member's, is refused with the newMemberImportRefusalCode.TelegramAccountSelected(telegram-account-selected) orDiscordAccountSelected(discord-account-selected).@domino-sdk/relay/serveraddsmemberImportVouchedAccountSchema,memberImportFullRowSchemaandmemberImportFullRowsInputSchema, with the typesMemberImportVouchedAccount,MemberImportFullRow,MemberImportFullRowInputandMemberImportFullRowsInput, for rows with every kind of vouched account;addRowstakesMemberImportFullRowInput[].memberImportAccountSchema,memberImportRowSchemaandmemberImportRowsInputSchemastay the X-and-wallet form. Member identity states carryimported: truefor an X, Discord or Telegram account an import attached, and so does a connectedManagedTelegramAccount.An imported X, Telegram or Discord account now pays tracker records from the moment it was imported. Before, a vouched X account had no link time, so its records were skipped as historical. Members imported before this release keep that unknown link time until they link their X account themselves.
-
f0d5b8e: Offers can be archived and restored.
management.offers.archiveandunarchive(POST /management/v1/offers/OFFER/archive|unarchive),domino offers archive|unarchive OFFER --reason, Console Settings → Resources and the offer's card in Console Rewards archive one in test, and a promotion carries it to live. Each returns{ resource, changed }, typed by the newofferLifecycleResultSchema, withresource.kindoffer. An archived offer leaves member offer reads and refuses redemption withoffer-paused; management reads keep it, with the newarchivedAtfield onOffer, and its redemptions can still be canceled.management.resources.list()anddomino resources listlist offers in a newoffersarray (managedResourceListSchema), beside the existing kinds. A rollback now archives an offer the earlier promotion did not carry. -
ea1f033: Revert an applied or stopped member import:
management.memberImports.revert(id, { reason })starts it, anddomino imports revert IMPORT --reason TEXTstarts it and waits. In the background, one row at a time, it reverses the applied rows' opening balances (taking only points members still have;unreversedPointslists the rest), revokes their imported completions (reasonimport-reverted, freeing the occurrence) and removes the wallets, accounts and identities they linked unless the member signed in with one since (keptLinks). Members stay in the project. A reverting or reverted import reads asappliedwithrevertset, and is reverted once; the sameactionIdanswers the import again. Rows applied before imports recorded their links keep them (linksUntracked). Reverting needspublishpermission and works in live from API keys and CLI sessions too. Console shows Revert import on applied and stopped imports.MemberImportgainsrevert: the reason, who asked and when, rows reverted so far, thenrevertedAtand theoutcome. -
9a7ad11: Stop a member import while it applies:
management.memberImports.stop(id)anddomino imports stop IMPORTlet the row being applied finish, apply no more rows, and end the import: it reads asappliedwithappliedAtnull andstoppedAtandstoppedByset, and its remaining rows stayready. A stopped import can't be applied again; import the remaining rows in a new import. Stopping needspublishpermission and works in live from API keys and CLI sessions too. Console shows Stop import on an applying import.MemberImportgainsstoppedAtandstoppedBy; checkstoppedAtto tell a stopped import from a fully applied one. -
df3a08c: A test member's clock moves by hours and minutes, or to an instant, so time-of-day rules can be tested: block edges, or midnight in the project's time zone.
management.testMembers.advance(member, input)takes{ actionId }with one ofdays(1–30, unchanged: project-calendar days that keep the time of day),hours(1–720),minutes(1–43200) orto, an ISO 8601 instant with an offset that must be later than the member's clock (else 422test-member-clock-past). From the CLI,domino test-members advance MEMBERtakes one of--days,--hours,--minutesor--to. The 90-day limit holds across units (422test-member-clock-limit):daysstill add up to at most 90 and land no later than the 90th date, and hours, minutes andtonever move the clock past 90 days from now.offsetDaysstill counts the days advanced withdays; hours, minutes andtoleave it unchanged, so read the member's date fromtoday.@domino-sdk/relay/serverexportstestMemberClockAdvanceSchemaandTestMemberClockAdvanceInputfor every form;testMemberAdvanceSchemaandTestMemberAdvanceInputstay thedaysform.
Patch Changes
- 6b5228b: Quests can announce a completion in a connected Telegram chat or Discord channel. List
telegram.postMessage({ chat, text, replyToTriggeringMessage })ordiscord.postMessage({ channel, text, replyToTriggeringMessage })in the new optionalannouncementsofdefineQuest, besiderewards(at most three; an array or a function of the settings). The text is a template with the placeholders{beneficiary},{author},{points},{rewards}and{quest}(announcementPlaceholders); people are mentioned through their linked account on that platform. Relay posts after the rewards commit, as a reward delivery with the newRewardDeliveryAction.PostMessageaction and an emptyroleId, retried with backoff and paced per chat; a failed post never undoes the rewards, andmanagement.retryRewardDelivery(id)retries one. Descriptions carry them in the new optionalannouncementsfield (announcementSchema).discord.channels({ postable: true })lists channels the bot can't post in as unavailable, andTelegramChatgains an optionalcanPost. The CLI's authoring reference documents announcements. - Updated dependencies [d258795]
- Updated dependencies [d41f5da]
- Updated dependencies [e899f47]
- Updated dependencies [8ee2000]
- Updated dependencies [f0d5b8e]
- Updated dependencies [6b5228b]
- Updated dependencies [ea1f033]
- Updated dependencies [9a7ad11]
- Updated dependencies [df3a08c]
- @domino-sdk/relay@1.60.0
1.59.0
Minor Changes
-
1d5a195:
management.attempts.acceptAnyway({ attempt, reason })(POST /attempts/<id>/accept-anyway,reviewpermission) accepts a rejected attempt anyway, for any member, reversing a reviewer's or an evaluator's rejection. The acceptance pays as a reviewer's approval would, with the normalcompletion.createdwebhook; the rejection stays in the attempt's events beside astaff-decisionwithdecision: "accept-anyway"and the reason, and the retry cooldown ends. Only the member's latest attempt on a quest that isn't archived qualifies (409attempt-superseded,quest-archived), and a cap the acceptance would break refuses it instead of rejecting it again (409completion-cooldown,budget-exhausted,completion-revoked,already-completed).management.attempts.getanswerssupersededBy, the member's newer attempt on the quest, or null. The CLI addsdomino attempts accept-anyway ATTEMPT --reason TEXT. -
a0c77e4: Archiving or unarchiving a group, pausing or resuming a tracker, and changing the project's schedule state take an optional reason, which the audit log records:
management.groups.archive(group, { reason })andunarchive(group, { reason }),management.trackers.pause(reference, { actionId, reason })andresume, andmanagement.schedules.setState(state, { reason }). A reason is a trimmed string of 1–500 characters, like the other lifecycle reasons; leaving it out works as before. The CLI adds--reason TEXTtodomino groups archive|unarchive,domino trackers pause|resumeanddomino schedules pause|archive|resume. Console asks for a reason in a dialog for each of these, and the project schedule menu no longer applies a state as soon as you pick it. -
424863f: Operators can restore an entitlement they voided.
management.entitlements.restore(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/restore,reviewpermission) anddomino entitlements restore <id> --revision <n> --reason <text>make avoidentitlementavailableagain at the tier an accepted completion still earns, and bring back any referral review the void settled. Restoring is refused with 409entitlement-statuswhen a completion revocation voided the entitlement or no accepted completion still earns it (the body names thecompletionto restore instead), when canceling an offer redemption voided it, or when a referral reward was voided before voids kept their reviews. Console Rewards and member detail offer Restore reward on a voided reward, and Audit history lists each restore with its reason. -
790aa5d: Operators can restore a completion they revoked by mistake.
management.completions.restore({ completion, reason })(POST /management/v1/completions/<id>/restore,reviewpermission) anddomino completions restore <completion> --reason <text>re-apply what the revocation recorded, not a fresh reward evaluation: the points it reversed are awarded again (points it could not reverse are not), its badges, group memberships, tiered rewards, Discord role grants, referral qualifications and streak milestone come back, and budgets stay spent. The result (CompletionRestoration) lists what came back. Restoring is refused with 409completion-not-revokedwhen the completion isn't revoked, and with 409occurrence-taken(naming thecompletionthat holds it) when the revocation freed the occurrence and the member completed it again.members.get(id).revokedCompletionslists up to 25 revoked completions, each with its revoke's reason and result. Console member detail and attempt detail offer Restore completion, and the attempt history and Audit history show the restore beside the revoke. -
95cb8d8: On-chain action checks on Solana.
ctx.wallet.transactions({ program, since, afterActivation?, mint?, address? })reads the successful, finalized transactions of the member's linked Solana wallets sincesincethat invokedprogramin a top-level or inner instruction, oldest first. It returns{ program, chain, mint, since, at, complete, transactions: [{ wallet, signature, slot, timestamp, programs, balanceChanges: [{ mint, delta }] }], observedAt }(WalletTransactions,WalletTransaction,WalletBalanceChange,walletTransactionsSchema), withmint"SOL"for lamports (rent included, the fee the wallet paid left out) anddeltaa signed integer string, or null when the member has no linked Solana wallet, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules. Withmint, only each wallet's associated token account for that mint is read.sincemay be at most 30 days before the evaluation (maxWalletTransactionLookback); each wallet's read fetches at most 1,000 transactions (maxWalletTransactions), and a wallet with more returns its newest ones withcomplete: falseinstead of throwing.WalletReader.transactionsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The program comes from the new
settings.solanaProgram({ label, default })setting, a base58 address (SolanaProgram,isSolanaProgram) stored as a text setting withpicker: "solana-program". Publication, Console settings edits and unarchiving check that the account exists on Solana mainnet and is executable.New pure helpers in
@domino-sdk/relay/authoringread one transaction:received(tx, mint)andspent(tx, mint)return the wallet's gain or loss as an integer string, or"0";swapped(tx, { into?, from? })checks that one mint went down and a different one went up;invoked(tx, program). The testing kit answersctx.wallet.transactions()fromfacts.wallet.transactions.
Patch Changes
-
aaa07ad: The SDK and CLI depend on Zod 4.6.5, whose schemas keep their methods on a shared prototype instead of binding about 7 KB of them to every schema instance. Importing the SDK takes far less memory: a Relay backend process that loads the server entry starts with about half the heap. Quest
settings.datetime()values, member-import times and X postcreatedAtvalues (including hand-written testing fixtures) still accept minute-precision instants such as2026-11-01T00:00Z, which Zod 4.6 alone rejects. An X post read without amediafield still gives an empty list.One tracker data schema change after a rebuild: a field declared as
z.number().min(0).int()now storesminimum: 0, so Relay refuses negative values at ingestion that the app's own schema already refused. Zod 4.3 stored the safe-integer minimum for that order of checks. -
Updated dependencies [1d5a195]
-
Updated dependencies [a0c77e4]
-
Updated dependencies [424863f]
-
Updated dependencies [790aa5d]
-
Updated dependencies [850a252]
-
Updated dependencies [7ac13ef]
-
Updated dependencies [5b94440]
-
Updated dependencies [95cb8d8]
-
Updated dependencies [aaa07ad]
- @domino-sdk/relay@1.59.0
1.58.0
Not published to npm. Its changes first reached npm in 1.59.0.
Minor Changes
- 88776b6: The operations journal no longer keeps webhook signing secrets.
domino webhooks createanddomino webhooks rotate-secretstill save the secret while the operation is incomplete, sodomino operations resumereturns the same one, but once the CLI has written it to stdout the saved result hassecretRedacted: trueinstead,domino operations showadds anotesaying to rotate it, and repeating the command with the same--operation-idfails withCONFLICT. A secret an older CLI saved is printed by the nextshoworresume, then removed. Newdomino operations forget <id>removes one saved operation, anddomino operations pruneremoves completed ones last changed more than--older-thandays ago (default 30).domino quests replay --outnow writes the capture to a new file with mode 0600 that replaces the destination, since it holds raw provider answers.
Patch Changes
- Updated dependencies [321763b]
- Updated dependencies [a2318dd]
- @domino-sdk/relay@1.58.0
1.57.0
Not published to npm. Its changes first reached npm in 1.59.0.
Minor Changes
-
7012d1c: Contract state and Cosmos staking reads in quest evaluators.
ctx.wallet.call({ call, args?, address? })calls onevieworpureEVM contract function once per linked EVM wallet, every call at one block, through dRPC on the chainsmanagement.walletBalanceChains()lists: state a contract keeps per address, such asstakedOf(address),getVotes(address)oruserInfo(uint256, address).{ wallet: true }inargsstands for each wallet's address. It returns{ call, chain, contract, returns, total, wallets: [{ address, value }], at, observedAt }(WalletCall,walletCallSchema), with integers as decimal strings andtotaltheir sum (null forboolandaddressreturns), or null when the member has no linked EVM wallet. A revert is anauthor-errorcarrying the contract's reason. The call comes from the newsettings.contractCall({ label, default })setting (picker: "contract-call"): the contract's CAIP-10 ID and a human-readable signature, such aseip155:8453:0x… function stakedOf(address) view returns (uint256)(ContractCall,isContractCall,parseContractCall,parseContractFunction). Onlyvieworpurefunctions with elementary parameters that return oneuint,int,booloraddressare accepted; publication, Console settings edits and unarchiving also check the chain.ctx.wallet.delegations({ chain, validator?, address? })reads the native staking delegations of the member's wallets linked on a Cosmos SDK chain Relay lists, at one block, in base units of the chain's staking denomination:{ chain, denom, validator, total, wallets, validators, at, observedAt }(WalletDelegations,walletDelegationsSchema), optionally only to one validator, or null when the member has no wallet on that chain.Both follow
ctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.WalletReader.callandWalletReader.delegationsare optional in the type so that existing test doubles compile; Relay supplies them to every evaluation, and the testing kit answers them fromfacts.wallet.callandfacts.wallet.delegations. Newcontract-stateandcosmos-stakersnippets accept members whose view call reaches a minimum, or who delegate at least a minimum, optionally to a configured validator. -
59a01d8: On-chain action checks on EVM chains.
ctx.wallet.events({ event, account, since, afterActivation?, where?, address? })reads the events of one contract whose indexed address argumentaccountis one of the member's linked EVM wallets, fromsinceto the chain's finalized block (its safe block on a chain without finality), through dRPCeth_getLogson the chainsmanagement.walletBalanceChains()lists. It returns{ event, chain, contract, since, at, finality, logs: [{ wallet, block, timestamp, tx, logIndex, args }], observedAt }(WalletEvents,walletEventsSchema) with decoded arguments, or null when the member has no linked EVM wallet, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.whereholds equality or{ gte }conditions on other arguments (WalletEventCondition);afterActivationskips events before the quest's activation.sincemay be at most 30 days before the evaluation (maxWalletEventLookback), and a read over more than 3,000,000 blocks or 1,000 logs (maxWalletEventLogs) throwsauthor-error.WalletReader.eventsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The event comes from the new
settings.contractEvent({ label, default })setting: a CAIP-10 contract, a space and the event's Solidity signature with argument names andindexedmarkers, such aseip155:8453:0x… Transfer(address indexed from, address indexed to, uint256 value)(ContractEvent,isContractEvent,parseContractEvent,ContractEventParts), stored as a text setting withpicker: "contract-event". An event without an indexed address argument is refused. Publication, Console settings edits and unarchiving also check that the chain reports a finalized or safe block and that the address holds a contract.The testing kit answers
ctx.wallet.events()fromfacts.wallet.events. A newonchain-actionsnippet rewards each new matching event since the later of the quest's activation and the wallet's link, with an optional minimum amount, keyed onchain:tx:logIndex. -
956b721: NFT holding checks on EVM chains and Solana.
ctx.wallet.nfts({ collection, address? })counts the NFTs of one collection the member's linked wallets of its family hold: on EVM, an ERC-721 contract'sbalanceOf(owner), or one ERC-1155 token ID'sbalanceOf(owner, id), through dRPC on the chainsmanagement.walletBalanceChains()lists; on Solana mainnet, the NFTs whose Metaplex metadata names the collection withverified: true(SPL Token and Token-2022 accounts holding exactly 1 of a 0-decimal, supply-1 mint; compressed NFTs and Metaplex Core assets are not counted). It returns{ collection, chain, standard, total, wallets: [{ address, count }], at, observedAt }(WalletNfts,walletNftsSchema;standardis"erc721","erc1155"or"metaplex"), or null when the member has no linked wallet of that family, and followsctx.wallet.balance()'s capture,author-errorandprovider-unavailablerules.WalletReader.nftsis optional in the type so that existing test doubles compile; Relay supplies it to every evaluation.The collection comes from the new
settings.nft({ label, default })setting, a CAIP-19 asset type such aseip155:1/erc721:0x…,eip155:8453/erc1155:0x…/1orsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/nft:<collection mint>(NftCollection,isNftCollection,parseNftCollection,NftCollectionParts), stored as a text setting withpicker: "nft". Publication and Console settings edits check it: an EVM chain and that the contract reports the standard through ERC-165supportsInterface, so a token contract or the wrong standard is refused when it is set; a Solana mint must be a Metaplex collection NFT, so a fungible mint, a mint without Metaplex metadata, a print edition or a member NFT is refused. Unarchiving checks it too, and a platform without its dRPC key refuses every EVM collection.ctx.wallet.balance()pointed at an ERC-721 or ERC-1155 contract now throwsauthor-errornamingctx.wallet.nfts()instead of reading a token count; it asks ERC-165 alongsidedecimals()on every token read.The testing kit answers
ctx.wallet.nfts()fromfacts.wallet.nfts. A newnft-holdersnippet accepts members whose linked wallets hold at least one NFT from the configured collection, on EVM or Solana. -
1770ada: A seventh starter,
loyalty: members join a campaign, earn points from its steps, join a members' group through a quest's group membership reward, and spend points on offers, one of them limited to the group withinGroup. Console offers it anddomino create --template loyaltycreates it.Starters are now an open list, so adding one no longer changes the SDK's types.
starterslists every starter as aStarterwith a plain stringid,offeredStarterSchemachecks an ID against it, andprojectCreateInputSchemaandrepositoryCreateSchemaaccept any offered starter;management.createProjectuses the first.starterTemplates,StarterTemplate,StarterId,starterIdSchema,projectInputSchemaandcreateRepositorySchemaare deprecated: they keep the first six starters and are removed in 2.0.0. To upgrade, replacestarterTemplateswithstartersandstarterIdSchemawithofferedStarterSchema, and type a starter ID asstring. -
cec850f: Create and change a tracker of every managed source without a deployment, not only X search.
management.trackers.createandmanagement.trackers.update(POST /trackers,POST /trackers/:tracker/update) take asource:{ kind: "x-search", query },{ kind: "discord-messages", text? },{ kind: "discord-reactions" },{ kind: "telegram-messages" },{ kind: "telegram-reactions" },{ kind: "youtube-shorts" }or{ kind: "tiktok-videos" }. Each is stored as the source its builder makes, such astelegram.reactions(), so quests read a data tracker as they read the same tracker in code.queryalone still creates an X search tracker; create takes exactly one ofqueryorsource. An update'ssourcereplaces the source's settings but never its kind, which is refused with 422validation-failed. Turning Discord message text off erases the text already stored.managedTrackerCreateSchemavalidates the new input, whiletrackerCreateSchemakeeps its X-only shape.managedTrackerSource(source)reads a published source by these names, andmanagedTrackerSourceKindslists them.domino trackers create KEY --source <kind>creates one from the CLI, with--text onfor Discord messages;domino trackers update TRACKER --text on|offturns message text on or off. Console Trackers → New tracker offers every source. -
94726d8: Organization automation keys (
dak_): a credential for CI and headless coding agents that no person owns. An organization administrator creates one in Console Settings → API keys for every project or a chosen list, with chosen permissions and an optional permission to create projects. It works asRELAY_MANAGEMENT_TOKEN, withdomino login --token-stdinand the Git credential helper, in the test environment only: live writes, administration and minting other credentials are refused.The management client adds
automationKeys(list,create,addSecret,revokeSecret,revoke) withautomationKeyInputSchema,automationKeySchema,issuedAutomationKeySchemaandautomationKeyPermissionSchema.accessSchemagains an optionalcredentialthat/accessanddomino whoami --jsonfill for an automation key with its kind and scope. -
6d9f0de:
telegram.messages()is a tracker source of the messages members post in the project's connected Telegram groups and supergroups, one record per message with the poster's Telegram account asauthor.dataisTelegramMessageData:chatId,chatType,messageId,replyToMessageIdandthreadId, never the text. Bots', service and channel messages are not recorded. In SDK 1.x it is a custom source on the reserved namespacetelegram-messages, which Relay alone writes. The newtelegram-contributionsnippet rewards posting once per member per day. -
35b445c: Unattended agent mode. Whoever launches an agent with nobody present sets
DOMINO_AGENT_MODE=unattended, anddomino doctor --jsonanddomino whoami --jsonreport it asagentMode. In that mode browserdomino loginrefuses with the newUNATTENDED_LOGIN_UNAVAILABLEcode, so remote work usesRELAY_MANAGEMENT_TOKEN, anddomino feedbackappends each report to./domino-feedback.jsonl(orDOMINO_FEEDBACK_FILE) instead of sending it;--submitorDOMINO_FEEDBACK=submitsends it. The Domino skill builds from the supplied brief without waiting, records the gaps it fills in CAMPAIGN.md, and ends with a report. -
8d11f64:
wallet.balances({ chain, token })is a tracker source of daily balance samples that Relay writes itself. Once per project-calendar day, at a time members can't predict, Relay reads one token's balance in every wallet linked by a member who joined the project, every wallet of a run at one pinned EVM block or Cosmos height (on Solana, every read must reach the run's pinned slot).chainandtokenare those ofctx.wallet.balance(). Each record is one wallet, keyed by its CAIP-10 account on the sampled chain, which is also itsauthor; each sample is a new observation whoseoccurredAtis the sample time and whosedataisWalletBalanceSample(amountin base units,decimals,block). A provider outage retries the same block later that day and never writes a zero, so a day with no successful sample breaks astreak({ days }). Reward holding for days withautomatic({ tracker, where: { path: "amount", gte: { setting: "threshold" } } })andstreak({ days }). In SDK 1.x it is a custom source on the reserved namespacewallet-balances, with the chain and token in the new optionalbalancefield of custom sources (walletBalanceSourceSchema); ingestion refuses the namespace, and a project and environment can have at most 5 such trackers (limit-reached).management.trackers.createand Console New tracker take{ kind: "wallet-balances", chain, token }(managedTrackerSourceSchema), anddomino trackers create --source wallet-balancestakes--chainand--token. The newtoken-hold-streaksnippet rewards holding a token for 7 days in a row.
Patch Changes
- dbffafb: On a Node release older than 24 the CLI now prints
Domino CLI needs Node 24 or newer. You have <version>.and exits 1, or theNODE_VERSION_UNSUPPORTEDerror with--json, instead of crashing with aSyntaxError. Arelay.jsonthat still has a starter's placeholder organization (local) and no checkout scope is refused with the newSCOPE_PLACEHOLDERcode, which says to rundomino checkoutor pass--organizationand--project;domino doctorreports it as ascopeerror.whoami,login,logout,create,checkout,initandfeedbackignore the placeholder instead of sending it. - 1e2de20: Agents can act as test members on staging. A staging link (
domino api POST /staging/link) minted by adomino loginsession, or by a personal API key or organization automation key that holdspublish, now opens the test toolbar, acting as that person or asautomation:<id>. Domino checks the key again on every toolbar request, so revoking it ends access. The Domino skill's campaign testing reference adds "Check participant flows on staging": mint the link, open it, sign the page in as a test member, and walk the app's own screens. - Updated dependencies [7012d1c]
- Updated dependencies [59a01d8]
- Updated dependencies [956b721]
- Updated dependencies [1770ada]
- Updated dependencies [cec850f]
- Updated dependencies [94726d8]
- Updated dependencies [6d9f0de]
- Updated dependencies [8d11f64]
- @domino-sdk/relay@1.57.0
1.56.0
Minor Changes
- 9ec9f2f: Publishing creates the groups code names. A deployment whose quest
groupMembership(group)reward orinGroup(group)prerequisite, offerinGroup(group)requirement, leaderboardgroupor referralinviterGroupnames a group the environment doesn't have now creates it, with no members and thedefineGrouphandle's title (the key when only a leaderboard or referral program names it), instead of refusing with 422validation-failed. Approving a promotion creates the groups live lacks the same way. The deployment preview and result, and the prepared promotion, list each such group as agroup-createdwarning, and the audit history recordsgroup-createdwith the deployment or promotion as its reason. An existing group keeps its title and members, and publishing never unarchives one: agroupMembershipreward naming an archived group still refuses. A project whose code uses groups no longer needsdomino groups createbefore its first deployment or promotion. - fbe8e64: Offers take prerequisites.
defineOffer({ ..., requires })accepts the prerequisites quests do,completed(quest),balanceAtLeast(ledger, minimum)andinGroup(group), at most 30;offerDefinitionSchemaandofferAuthoringSchemagain an optionalrequires. Publication refuses a required quest or ledger the deployment doesn't know. Redeeming as a member who misses one fails with 409prerequisites-incompleteandmissing, the shape quest admission uses, before any points or stock are spent.Offergainsrequiresandmissing: for a signed-in member,relay.offers.list()anduseOffers()list the prerequisites they have not met, empty for a read without a member or an offer withoutrequires.availabilitykeeps its four kinds. Console Rewards shows an offer's requirements on its card, and a promotion carries them to live. - cba70f4: Add the organization's billing read.
management.billing.organization()anddomino billing organization(GET /management/v1/organization/billing) are open to any member of the organization and need no project. They list the projects the caller may read, each with its plan,liveandliveStateasmanagement.usage.plan()reports them, and its Stripe subscription. They also return the organization's Free projects against their limit (freeProjects), the free AI allowance its Free projects share (freeAi), the monthly AI allowance across the organization, the trial, whether plan limits are enforced, and whether the caller may open the customer portal (billing).management.billing.organizationPortal()(POST /management/v1/organization/billing/portal) opens the customer portal asmanagement.billing.portal()does, returning to Console's organization billing page.organizationBillingSchemaand theOrganizationBillingtype are exported from@domino-sdk/relay/server, with a reader inmanagementResponses. - 6458ebe: Add the project's plan read.
management.usage.plan()anddomino usage plan(GET /management/v1/usage/plan,readpermission) return the project's plan and where it came from (source), whether it may serve live and why (live), live's notice or offline state (liveState), the organization's one live trial (trial), whether the plan includes a custom domain, the AI builder balance by pool with when it renews (ai), the Stripe subscription asmanagement.billing.read()reports it, and whether plan limits are enforced. Unlikemanagement.billing, it is open to anyone who can read the project.projectPlanSchemaand theProjectPlantype are exported from@domino-sdk/relay/server, with a reader inmanagementResponses. - 62c3791: Add TikTok creator accounts and the
tiktok.videos()tracker source. A signed-in member links their TikTok account through TikTok's consent screen withrelay.auth.creatorAccounts.connect("tiktok")(useCreatorAccounts()in React); the account's subject is itsopen_id, its profile a display name and avatar, andknownCreatorAccountProvidersnow lists"tiktok". Operators read and unlink it like a YouTube channel.tiktok.videos()from@domino-sdk/relay/authoringreads the public videos on the accounts members linked, only with each member's own token, everyrefreshSecondsof the tracker, as content with aTikTokVideoBody(title,description,durationSeconds,width,height,createdAt,shareUrl,isAigc) and the metricsviews,likes,commentsandshares, socontent()quests,performanceand engagement rules use it. A known video the account no longer returns is a content removal; an account whose token needs reconnecting is not read, asprovider-unavailable. It is a custom source on the reserved namespacetiktok-videos, which ingestion refuses. Both are inert until Domino configures TikTok. Thetiktok-videoscapability and the participant and authoring references describe them. - a533f93: Add creator accounts and the
youtube.shorts()tracker source. A signed-in member links the YouTube channel they publish from as a creator account:relay.auth.creatorAccounts.list()(GET /v1/auth/creator-accounts),connect(provider)anddisconnect(provider), anduseCreatorAccounts()in React. A creator account is never a sign-in method, linking another channel replaces the current one, and the account's subject is the channel ID picked on Google's consent screen. Each entry mirrorsAccountConnectionwith the channel'sprofile;provideris an open string, withknownCreatorAccountProvidersin@domino-sdk/relay/schemalisting"youtube", soCreatorAccount,CreatorAccountsand their schemas sit beside SDK 1.x's closed provider lists. Operators read them onmanagement.members.get(member).creatorAccountsandmanagement.members.creatorAccounts(member), andmanagement.members.unlinkCreatorAccount(member, { provider, subject, revision, reason })unlinks one withreviewpermission;managedCreatorAccountSchema,managedCreatorAccountsSchemaandmemberCreatorAccountUnlinkInputSchemaare exported from@domino-sdk/relay/server.youtube.shorts()from@domino-sdk/relay/authoringreads the Shorts on the channels members linked, everyrefreshSecondsof the tracker, as content with aYouTubeShortBody(title,description,tags,durationSeconds,publishedAt) and the metricsviews,likesandcomments, socontent()quests,performanceand engagement rules use it. It is a custom source on the reserved namespaceyoutube-shorts, which ingestion refuses. Both are inert until Domino configures YouTube. Thecreator-accountsandyoutube-shortscapabilities and the participant and authoring references describe them.
Patch Changes
- 42784fb: The hosted-app skill reference notes that every
domino.pageaddress answers/.well-known/security.txtitself with Domino's abuse contact, so an app never receives that path there; a custom domain still forwards it. - a88ba4d: The capability map and the Domino skill's automation reference describe Domino's MCP server at
https://mcp.domino.run, which operators connect to Claude or ChatGPT: reads of projects, quests, campaigns, submissions, members, leaderboards, offers, usage, analytics and audit history in test and live, and test-member and review tools that write in test only. Seedomino capabilities mcp-server --jsonand the "Claude and ChatGPT" section ofdomino reference automation. - Updated dependencies [9ec9f2f]
- Updated dependencies [fbe8e64]
- Updated dependencies [cba70f4]
- Updated dependencies [6458ebe]
- Updated dependencies [62c3791]
- Updated dependencies [a533f93]
- @domino-sdk/relay@1.56.0
1.55.0
Minor Changes
-
5c70548: Add monthly active member usage.
management.usage.activeMembers()anddomino usage active-members(GET /management/v1/usage/active-members,readpermission) return the project's live active members this UTC month (month,activeMembers), its plan and the plan'slimit(null when the plan has none), and the six earlier months inhistory, newest first. An active member was active in the month on a UTC day after the day they joined the project: a signed-in participant API request, which includes their attempts and redemptions. Imports, server-reported tracker records and test members never count.activeMemberUsageSchemaandactiveMemberMonthSchemaare exported from@domino-sdk/relay/server, with a reader inmanagementResponses. -
65f3a9a: Add four metrics to the analytics catalog.
groups.joinsandgroups.leavescount members added to and removed from a group, by when the change happened, without repeats that left membership as it was or memberships recorded without a date; theirgroupbreakdown and filter mean the change's group.trackers.recordscounts tracker trigger outcomes by when the record was observed, as each outcome is now, with anoutcome(admitted,skipped) ortrackerbreakdown and atrackerfilter that keeps the tracker's own outcomes.leaderboards.settledParticipantscounts the rows of pending and approved leaderboard settlements by each settlement's cutoff, with aleaderboardbreakdown and filter, and draws asnumber,barortable.analyticsBreakdownsgainsgroup,tracker,outcomeandleaderboard, andanalyticsFiltersgainsleaderboard. A dashboard panel naming a leaderboard the environment doesn't have fails the deployment withunknown-reference. -
8f12977: Add the closed metric catalog.
management.analytics.catalog()anddomino analytics metrics(GET /management/v1/analytics/catalog) list every metric with its definition, kind (count,distinctorrate), the breakdowns and filters it accepts, its allowed charts and whether it needs a point ledger;analyticsCatalogand theAnalyticsMetricId,AnalyticsBreakdown,AnalyticsFilterandAnalyticsCharttypes are exported from@domino-sdk/relay/server.management.analytics.query({ metric, range, breakdown?, filter?, ledger?, limit? })anddomino analytics query --metric ID [--range] [--breakdown] [--filter name=a,b]... [--ledger] [--limit](GET /management/v1/analytics/query,readpermission) read one metric bucketed likeanalytics.read, and with no breakdown or filter return the same numbers. A breakdown by quest, offer, referral program, rejection source or rejection reason has a row per matching resource (archived ones only with activity), sorted by total and capped bylimit(1 to 100, default 100) with the rest summed intoother. Filters narrow to quests, offers or programs by key, the quests a tracker triggers, or members now in a group. The new ratesquests.rejectionRateandreferrals.qualificationRatearenullbelow 10 in the denominator. A query the catalog doesn't accept is a 422 withunknown-metric,breakdown-not-accepted,filter-not-accepted,ledger-required,ledger-not-accepted,invalid-limitorunknown-reference, and a missing group isgroup-not-found.analyticsCatalogSchema,analyticsMetricQuerySchemaandanalyticsMetricResultSchemaare exported, with readers inmanagementResponses. -
c91ba35: A repeatable quest can space out a member's completions. Set
completionCooldownMs(1,000 to 604,800,000) on a quest members submit that completes more than once, or Completion cooldown on a quest made in Console. After a completion of any key is accepted, a new attempt is refused with 409completion-cooldown(RelayErrorCode.CompletionCooldown) untilretryAt; when the retry cooldown also runs, the later one is named. A quest that completes once refuses the field. Quest progress andQuestViewcarrynextAvailableAt, when the member may next submit: the latest of the retry cooldown's end, the completion cooldown's end and an exhausted budget's next reset, omitted when nothing will reopen the quest.questStatusreportscompletion-cooldown,canSubmitis false until then, anduseQuestrenders again when it ends. Catalog quest instances take an optionalcompletionCooldownMs(nullfor none). A quest without the field keeps today's behavior. -
6b946d8: Add dashboards: a
relay.jsondefinition kind built from the metric catalog. Default-exportdefineDashboard({ key, title, description?, defaultRange?, position?, sections })from@domino-sdk/relay/authoring, typed so each panel accepts only the breakdowns, filters and charts its metric allows, and list it underdashboards: [{ entry, exportName? }]in relay.json. A deployment refuses a dashboard with a coded 422 (unknown-metric,breakdown-not-accepted,breakdown-required,filter-not-accepted,chart-not-accepted,ledger-required,ledger-not-accepted,unknown-reference,referral-program-required,duplicate-key,too-many-panelsortoo-many-dashboards), publishes a new version only when the definition changed, and reports each one in the preview'sdashboardsascreated,updatedorunchanged.management.dashboards.list({ includeArchived? })anddomino dashboards list(GET /management/v1/dashboards) list them;management.dashboards.read({ key, range? })anddomino dashboards get KEY [--range](GET /management/v1/dashboards/KEY) return the definition and every panel'sanalytics.queryresult, with a failing panel's ownerror(group-not-found,panel-timeout) beside the others.management.dashboards.archive|unarchive(key, { reason })anddomino dashboards archive|unarchive KEY --reason(POST /management/v1/dashboards/KEY/archive|unarchive) archive and restore one in test, returning{ dashboard, changed }, and promotions carry dashboards to live by key as changes of kinddashboard. The deployment preview'sdashboardsis optional, so values built for older SDKs still fit.dashboardDefinitionSchema,dashboardAuthoringSchema,normalizeDashboard,dashboardIssueanddashboardPreviewSchemaare exported from@domino-sdk/relay/schema, anddashboardListSchema,dashboardReadSchemaanddashboardLifecycleResultSchemafrom@domino-sdk/relay/server, with readers inmanagementResponses. -
3a8a888: Engagement points land in the leaderboard window they were measured in. Each engagement settlement's point entries now take effect when the content sample that measured their growth was observed, instead of when the daily settlement paid them, and budgets charge each entry to the period of its
effectiveAt. When the growth between two samples spans a leaderboard window or budget period boundary, Relay splits it there in proportion to time (rounded down, the remainder to the later part), one entry per side. Before, every earned board over engagement points ran about a day late.A settling leaderboard whose score counts an engagement rule's points now takes a closing measurement before it freezes. While it closes, its settlement reads as
pendingwith no rows in the review reads (settlementReviews,settlementReview), with a new optionalclosingfield holdingstartedAtanddeadline: Relay asks scheduled trackers to resample the content, a sample observed within 30 minutes after the cutoff counts as measured at it, and the settlement freezes when everything is measured or the wait ends. Settlements gainunmeasured: content frozen at its last good sample, withcontent,memberandobservedAt.management.leaderboards.retrySettlementMeasurement(id, { actionId? })anddomino leaderboard-settlements retry-measurement SETTLEMENT(POST /management/v1/leaderboard-settlements/:id/retry-measurement,reviewpermission) close such a settlement again for another 30 minutes. Approving a closing settlement fails withsettlement-closing; retrying one with nothing unmeasured fails withsettlement-measured. The oldersettlementsandsettlementreads omit closing settlements. -
6cc08fa: A member can greet themselves by first name. Each entry of
relay.profiles.mine().optionsgainsgivenName: the first name Google sent at sign-in (OIDCgiven_name), null for X, Discord and accounts without one. Older servers' answers still parse, withgivenName: null. Only the member's own profile carries it; profile reads by ID and leaderboard rows don't. Existing Google accounts get it on their next sign-in. Operator member reads are unchanged. -
921e9d7: Add
dominoHosting({ command })to@domino-sdk/relay/vite. It returnscloudflare, options for the app's owncloudflare()call from@cloudflare/vite-plugin(the Worker in Vite'sserverenvironment, and, only when serving, the variablesdomino devsets forwarded to the local Worker, empty when unset), andserver, Vite's server settings (port fromDOMINO_APP_PORT, 5173 by default, and theDOMINO_PREVIEW_ORIGINhost with HTTPS hot reload when it is set). The SDK does not depend on the Cloudflare plugin.Add
domino build --hosting-artifacts, which writes the quest and catalog artifacts the hosting builder reads to.domino-build/quests.jsonand.domino-build/catalog.jsonbesiderelay.json.The starters use both: their
vite.config.tsis their own plugins plusdominoHosting(), andscripts/build-domino.mjsgives way tovite build && domino build --hosting-artifacts. Existing apps keep working unchanged. -
a801a1d: Leaderboard settlements can split a pool prize by score.
settle.poolis{ amount, unit, ledger?, minScore? }:amountis the whole pool in base units as a decimal string, andunitis"points"or{ code, decimals }for a token or currency. A points pool names theledgerit pays into (a ledger handle or key indefineLeaderboard) and holds at most 2,147,483,647 points; other units take no ledger. With a pool,settle.prizesmay be empty (defineLeaderboardlets you leave it out), and a board can have both. When the window freezes, Relay splits the pool over the eligible frozen rows in proportion to score, in exact integer math: members scoring 0 or less, or at or belowminScore, get nothing, each pool share rounds down, and the leftover base units go one each to the largest remainders, earlier positions first, so the shares sum exactly to the pool. With no eligible score the whole pool stays unallocated.Settlement reads (
settlement,settlementReview) gainpool, withallocatedandunallocated, and each row gainssharein base units; both are absent from settlements without a pool. Approval pays a points pool's shares as points in each member's prize completion, and revoking the completion reverses them. Relay pays no other unit:management.leaderboards.settlementExport(id)(GET /management/v1/leaderboard-settlements/:id/export,readpermission) anddomino leaderboard-settlements export SETTLEMENT(CSV, or--json) list each member's score, rank, share in base units and whole units, and verified linked wallets, as the operator's payout list. Console shows each winner's pool share before approval. -
cbdcdc0: Add the plan limits in test and setup, where plans apply to the organization. A sign-in that would add a real person to a test environment that already has its plan's number of real people fails with
limit-reached(403,limit: "real-people-in-test",count,maximum); people who already joined, test members and live sign-ins are unaffected. Creating a project, or transferring a Free project in, fails withlimit-reached(403,limit: "free-projects") when the organization already holds 10 Free projects. Adding a custom domain needs the Growth plan or above and otherwise fails withplan-required(403,feature: "custom-domain").management.usage.realPeopleInTest()anddomino usage real-people-in-test(GET /management/v1/usage/real-people-in-test,readpermission) return the test environment'srealPeople, the project'splan, itslimitand whether it isenforced;realPeopleInTestUsageSchemais exported from@domino-sdk/relay/server, with a reader inmanagementResponses. The CLI's error JSON keeps alimit-reachedorplan-requiredrefusal's detail indetails. -
823a691: Member accounts that Relay reads in the background with the member's own OAuth tokens now report when those tokens stop working. In management member reads (
management.members.get,list,lookup, and thepersonon attempts) such an account carriesstatus: "connected", orstatus: "needs-reconnect"with areasonofrevoked,expiredormissing-scope; accounts Relay keeps no tokens for carry neither field.domino members getand the other CLI results include the same optional fields. The participant client'srelay.auth.accounts()anduseAccounts()report the account'sstatusas"reconnect"until the member links it again. Relay refreshes kept tokens before they expire. No provider reads accounts in the background yet. -
a4c93bc: Add Stripe billing for a project.
management.billing.checkout({ plan, interval })anddomino billing checkout --plan launch|growth|scale [--interval month|year](POST /management/v1/billing/checkout) return a Stripe Checkout URL that subscribes the project;management.billing.portal()anddomino billing portal(POST /management/v1/billing/portal) return the organization's Stripe customer portal. Both need an organization administrator, not an API key (403permission-required). While billing is staff-only, every organization but Domino's own is refused with 403billing-staff-only. A project whose subscription holds its plan is refused another checkout with 409subscription-exists, and the portal answers 409no-customerbefore the organization's first checkout; a deployment without Stripe answers 503not-configured.management.billing.read()anddomino billing show(GET /management/v1/billing,readpermission) return whether billing is open to the caller (available,reason) and, when it is, the project'ssubscription:plan,interval, Stripe'sstatus,currentPeriodEnd,cancelAtPeriodEnd,lastInvoiceandlivemode.billingCheckoutInputSchema,billingSessionSchema,billingSubscriptionSchemaandprojectBillingSchemaare exported from@domino-sdk/relay/server, with readers inmanagementResponses. -
b8348fd: Change a project's Stripe plan, cancel and resume.
management.billing.changePlan({ plan, interval })anddomino billing change --plan launch|growth|scale [--interval month|year](POST /management/v1/billing/plan) move a paying project's subscription: a higher plan or annual billing applies now with the prorated difference invoiced at once, and a lower plan or monthly billing is held for the renewal. The answer isbillingPlanChangeSchema:timing(now,renewalornone) andinvoiceUrl, set when the immediate charge needs the customer and nothing changes until it is paid.management.billing.cancel()andmanagement.billing.resume()(domino billing cancel,domino billing resume;POST /management/v1/billing/cancel|resume) set the subscription to end when its period does, or take that back, and return the project's billing. A project without a subscription that holds its plan is refused with 409no-subscription, and a plan change while the subscription is set to cancel with 409subscription-canceling. The subscription now carriesscheduledChange, the plan, interval and date of a change held for the renewal, or null. The customer portal no longer changes or cancels plans; it keeps cards, invoices and billing details. -
c055c43: A project's Stripe subscription follows the project. Deleting a paying project sets its subscription to end with its period, and restoring it resumes the subscription; purging ends it at once. A transferred project keeps its plan until the old organization's period ends, and
management.billing.checkout()(domino billing checkout) subscribes it in the receiving organization with no charge before then. The subscription now carriesendedFor:project-deleted,project-transferredor null. -
f89ea5d: Buy AI builder credit, and see an overdue subscription.
management.billing.topup({ quantity })anddomino billing topup [--quantity 1-10](POST /management/v1/billing/topup) return a Stripe Checkout URL buyingquantitypacks of $10 AI builder credit for the project, on any plan; the credit is added once the payment is in, once per purchase.billingTopupInputSchemaandbillingTopupMaxQuantityare exported from@domino-sdk/relay/server. The subscription now carriesunpaidwhile an invoice is overdue:since(when it first failed),promotionsRefusedAt(14 days later, when the organization's new promotions are refused withplan-requiredandreason: "unpaid") andofflineAt(45 days later, when the project's live goes offline until it is paid), or null. -
8a5b7d3: Add
settings.amount({ label, default, decimals? })andsettings.chain({ label, default })to@domino-sdk/relay/authoring. An amount setting holds a non-negative base-unit integer string such as"1000000000000000000"; withdecimals, Console shows and edits it in whole tokens. A tracker filter bound may name an amount setting (gte: { setting: "minimum" }) and compares it exactly, so 18-decimal thresholds fit. A chain setting holds"solana", Solana mainnet's CAIP-2 ID or an EVM chain ID such as"eip155:8453", typed as thectx.wallet.balance()query'schain. Both are text settings (picker: "amount"or"chain"; text fields gain an optionaldecimals), and publication and Console edits refuse other values.@domino-sdk/relay/schemaexportsisAmount,isBalanceChain,isBoundSetting,textSettingSchemaand theAmountSetting,BoundSettingsandBalanceChaintypes. Thetoken-holderexample and snippet use both settings and need noWalletBalanceQuerycast; their minimum is now in base units. -
4aaea1a: X tracker refresh policy. Tracker status gains
pauseReason(operator,x-budget-exhaustedortest-window-ended; null when not paused),resumesAt(when a tracker paused on its X budget resumes on its own) andeffectiveRefreshSeconds(the interval a scheduled tracker actually runs at:refreshSeconds, but no shorter than the project's plan allows, 6 hours below Enterprise). All three are optional, so older servers' answers still parse. Known X posts are re-read every 6 hours for 48 hours after they were posted, daily until day 7, then never. A deployment whose X search tracker asks for less than the plan's minimum warnstracker-refresh-below-plan-minimumand still publishes. A spent monthly X budget pauses X trackers withx-budget-exhausted(live only while plan limits are enforced, test always) until the budget renews, and resuming one meanwhile answers 409x-budget-exhausted; a test X tracker pauses withtest-window-endedafter 14 days until resumed.domino trackers create|update --refresh-secondshelp names the plan's minimum.
Patch Changes
-
283a1a2: The Domino skill has an analytics reference (
domino reference analytics): how to answer "how is it going" fromdomino analytics getanddomino analytics query, choose catalog metrics from a project's goals, and build, check and explain dashboards. Theproject-analyticsanddashboardscapabilities point to it. The referral starter ships a Referrals dashboard with sign-ups, qualified referrals by program and the qualification rate. -
21a4386:
domino check,domino buildanddomino deployrefuse quests that declare one budget key with a different scope, period, limit or cost, naming both quests and the fields that differ.checkreports it as the error diagnosticBUDGET_DECLARATION_CONFLICT;buildanddeployfail withBUNDLE_FAILED. Relay also comparescostwhen it checks a deployment's shared budgets. -
dbc4e34:
dominostarts faster.domino --versionand--helpdrop from about 0.4 s to under 0.1 s, and commands that call the management API through the CLI's own transport (domino api,domino builds,domino whoami) from about 0.44 s to about 0.29 s. The CLI now loads the SDK, zod and the system certificate store only when a command needs them. Output, exit statuses and--jsonresults are unchanged. -
5a8a0a0: CLI paper cuts.
domino stage --commitaccepts a short or uppercase SHA: a short one resolves to the full commit in the checkout, failing with Git's reason when it is unknown or ambiguous; a--request-idresume still takes the full SHA the build request printed.domino apistrips one/management/v1the path already starts with, sodomino api POST /management/v1/staging/linkreaches/staging/link(a one-line note on stderr says so outside--json). A reader that closes stdout early, as indomino --help | head, ends the CLI with status 0 instead of an unhandled EPIPE.domino test-members act --wait [--timeout SECONDS]reads the attempt until it is no longer queued, evaluating or deferred and prints it with its status, reason and code; without--waitit prints the queued attempt as before. The deprecation notes of baredomino status,questsandreleasesname the typed read for each use, the help says that--jsonstdout carries exactly one JSON value while progress events go to stderr as NDJSON, anddomino build --helprecommends--out FILE --jsonfor a summary. Both packages now ship theirCHANGELOG.md. -
9e5c5c2: Going live needs a plan where plans apply to the organization. Approving a promotion for a project without a plan starts the organization's one 14-day live trial when it is unused, and otherwise fails with
plan-required(403), whosetrialsays where the trial is. When a trial ends without a plan, live keeps serving for 7 days, then goes offline: participant requests to live fail withlive-offline(403) and the live app answers a "This app is offline" page, until the project gets a plan. The error reference describes both codes. -
a242e19: The errors reference lists
ai-allowance-exhausted: a 402 when a project's AI builder balance is empty and the Playground cannot start. -
ea4814c: Add the
trackersreference to the Domino skill, sodomino reference trackersserves tracker collection options, record filters and attribution, tracker trigger rules and outcome codes, Discord record fields, and engagement accounting. -
c656f61: A quest's
completioncan be a function of the policy builders:completion: ({ keyed }) => keyed(({ tracker, settings }) => tracker.record.data.messageId). Its key callbacks,keyed(...)'s key andprogress(...)'sdistinct, read aQuestKeyedContexttyped from the quest:trackeris non-null and typed for a tracker trigger, andsettingshas the quest's settings, so thetracker!cast is no longer needed. The builders it receives areonce,keyed,streakandprogress.completion: keyed(...)built inline keeps working and keeps itsKeyedContext. Key callbacks now also receive the quest's parsedsettingsat runtime.runQuestreportsrun.diagnostics(andcreateTestContextitsdiagnostics): acompletion-key-ignoredentry when the evaluator returns acompletionKeythe completion policy ignores. Onlykeyed(...)uses it;once(),streak(...),progress(...)and content quests key completions themselves. The decision is unchanged, and hosted attempts record nothing new. The Domino skill's authoring and tracker references describe the builder form and which policies usecompletionKey. -
Updated dependencies [5c70548]
-
Updated dependencies [65f3a9a]
-
Updated dependencies [8f12977]
-
Updated dependencies [5a8a0a0]
-
Updated dependencies [c91ba35]
-
Updated dependencies [6b946d8]
-
Updated dependencies [3a8a888]
-
Updated dependencies [5ae31db]
-
Updated dependencies [d463895]
-
Updated dependencies [7ff5984]
-
Updated dependencies [6cc08fa]
-
Updated dependencies [921e9d7]
-
Updated dependencies [6156dec]
-
Updated dependencies [a801a1d]
-
Updated dependencies [cbdcdc0]
-
Updated dependencies [823a691]
-
Updated dependencies [cc459ef]
-
Updated dependencies [a4c93bc]
-
Updated dependencies [b8348fd]
-
Updated dependencies [c055c43]
-
Updated dependencies [f89ea5d]
-
Updated dependencies [8a5b7d3]
-
Updated dependencies [c656f61]
-
Updated dependencies [4aaea1a]
- @domino-sdk/relay@1.55.0
1.54.0
Minor Changes
-
ee9e5f1: Add
management.analytics.read({ range, ledger })anddomino analytics get [--range 7d|30d|90d|all] [--ledger KEY], which read a project's activity in one environment fromGET /management/v1/analyticswithreadpermission. A range ends now and is bucketed by project-calendar day (7d,30d) or Monday week (90d,all, which starts on the day of the earliest dated record: the first membership, or an older imported completion). Each metric (sign-ups, active members, attempts, completions, rejections, attempts sent to staff review, one point ledger's points awarded, spent and net and members earning, fulfilled offer redemptions, accepted and qualified referrals) has a total, a value per bucket and the previous range's total. Referrals also reportacceptedQualified, how many of the range's accepted referrals have qualified since, for a share that never exceeds 100%. Each quest also has its decisions, rejection rate, top 5 rejection reasons and rejections by source. Offers and referrals appear only when the project has them, and a test environment section only in test.domino analytics getprints a readable summary, and the read with--json;analyticsReadSchemaandanalyticsQuerySchemaare exported from@domino-sdk/relay/server.managementResponsesfrom@domino-sdk/relay/servernow builds each reader on first use, asmanagementResponseSchemasdoes, so importing the entry retains about 3.5 MiB less heap. Each reader is built once and stays writable. -
63f7a14: Custom domains. An organization admin can attach a domain they control, such as
quests.example.com, to a project's live hosted app. Domino shows a CNAME tocname.domino.pageand a_domino.TXT record that proves the domain, issues its certificate, and serves the live app there once both are in place. While the domain is active, page loads on<project-slug>.domino.pageredirect to it.domino domains add|list|get|recheck|removeand Console Settings → Domains attach, check and remove the domain, and show each DNS record against what public DNS returned and Cloudflare's statuses word for word.management.domains.list(),.get(id),.add(hostname),.recheck(id)and.remove(id).- Root domains, wildcards, IP addresses and Domino's own domains are refused with
invalid-hostname. A project has one custom domain. - The
hosted-custom-domainscapability is now supported.
-
f5bd728: Test members can accept a referral invitation:
management.testMembers.acceptReferral(member, { code })or{ inviter }(another test member, whose invitation code is used), anddomino test-members accept-referral MEMBER --code CODEor--inviter MEMBER. The acceptance runs as a signed-in member's would, with the same refusals, qualification and rewards, in the test environment only, and the audit names the operator.
Patch Changes
-
e735ab8:
domino migraterenamesidtokeyin referral bonuses and milestones, tiers, andbudget()calls, andbalancetoledgerin referral bonuses, leaderboards, and budget costs. Strings where v1 takes a handle (quests,qualifiedBy,ledger, prerequisites) get aHANDLE_REFERENCE_REQUIREDerror naming the field instead of passing silently, and migrate no longer says "No automatic changes needed" while such errors remain. -
50246d7: Hosted app builds no longer set
VITE_DOMINO_STAGING. It wastruein every build, including the builds the live app serves, so an app that gated test tools on it showed them in live. Builds now see onlyCI=true. To tell staging from live, callgetHostEnvironment()from@domino-sdk/relay/browserin the browser (static and Worker apps), or readDOMINO_ENVIRONMENTin a Worker's server code.Staging moved from
staging-<project-slug>.domino.runto<project-slug>--staging.domino.page. The old staging addresses, and development previews ondomino.run, now answer410 Gonewith a page naming the new address; they do not redirect. Links printed with an old staging address, such as QR codes, need the live address instead. -
Updated dependencies [ee9e5f1]
-
Updated dependencies [63f7a14]
-
Updated dependencies [50246d7]
-
Updated dependencies [f5bd728]
- @domino-sdk/relay@1.54.0
1.53.1
Patch Changes
- 500f752:
domino checkanddomino migrateresolvedefineLedgerhandles, defined in the same file or imported, to their keys, sopoints(handle, n)no longer warnsLEDGER_REFERENCE_DYNAMIC. They also check v1ledger:references in leaderboards, referral bonuses andbudget({ cost: { ledger } }), and count a ledger published inrelay.jsonpointLedgersas{ "entry", "exportName" }as declared, sodomino migrateno longer proposes an inline duplicate for it. A reference that really needs code to resolve suggests publishing adefineLedgerexport. - @domino-sdk/relay@1.53.1
1.53.0
Not published to npm. Its changes first reached npm in 1.53.1.
Minor Changes
- 1b789ce: Test members can hold real X and Discord accounts.
management.testMembers.linkAccount(member, { actionId, provider, id }), or{ actionId, provider: "x", username }resolved through X, links a public account to a test member without OAuth, andunlinkAccount(member, { actionId, provider, id })unlinks and frees it (domino test-members link-accountandunlink-account). Quest code'sx.profile(),follows(),reposted()and Discord role reads then answer for the real account, tracker records it authors are attributed to the member, andtestMembers.recordposts as it instead of refusing withconflict. An account another member holds is refused withaccount-linked-elsewhere; when its owner signs in with it or connects it, it moves to them. Reset unlinks it. Relay never writes to a test-linked account: a Discord role reward for one creates no delivery and is never sent, and the attempt'shistoryentry carriesrewardsNotSent: [{ reason }]. Test member results include an optionalaccountslist; managed member identities andmembers.oauthAccountsmark these accounts with an optionaltestLinkedflag.
Patch Changes
- Updated dependencies [1b789ce]
- @domino-sdk/relay@1.53.0
1.52.1
Patch Changes
- b857dc5: The member imports skill reference sends live imports to Console's Members > Imports, where member imports now open. Console no longer has a separate Member imports page.
- @domino-sdk/relay@1.52.1
1.52.0
Minor Changes
- 9fadb80: Evaluator rejections take an optional code.
decision.reject({ reason, code })from@domino-sdk/relay/authoringnames the cause with a stable key, such aswrong-answers, that stays the same when the reason's wording changes. Relay stores it as the attempt'scode, returned bymanagement.attempts.getandmanagement.attempts.list,domino attempts list|getand Console's attempt detail beside the reason; responses to members carrycode: nullfor an evaluator-rejected attempt. A code uses lowercase letters, digits and hyphens, at most 64 characters, and cannot be one of the codes Relay writes itself, now exported asreservedAttemptCodesfrom@domino-sdk/relay/schema; an invalid one fails the evaluation, andrunQuest, with an error naming the rule. A content quest'sperformance.evaluateaccepts the samecode. Rejections without a code are unchanged.
Patch Changes
- Updated dependencies [9fadb80]
- @domino-sdk/relay@1.52.0
1.51.0
Patch Changes
- Updated dependencies [4dddeb0]
- @domino-sdk/relay@1.51.0
1.50.0
Minor Changes
-
2ecde12: An operator can pass a quest for a test member without running its check, or accept a test member's rejected attempt anyway, to reach the quests behind a check that can't be satisfied in test.
- New
management.testMembers.pass(member, { actionId, quest, completionKey? })admits an attempt on a manual quest as a submission would, with no input, then accepts it as the operator. Prerequisites, availability, enrollment and the retry cooldown still refuse it, and the completion policy, budgets and rewards apply as for an accepted check: a progress quest counts one step, and a keyed quest withoutcompletionKeygets a fresh key. From the CLI,domino test-members pass MEMBER QUEST [--completion-key KEY]. - New
management.testMembers.acceptAnyway(member, { actionId, attempt })accepts a rejected attempt as the operator, such as a tracker-triggered attempt its check rejected. The attempt keeps its rejection in its history, and when it is the member's latest attempt on the quest, its retry cooldown ends. Any other status is refused with 409attempt-not-rejected. From the CLI,domino test-members accept-anyway MEMBER ATTEMPT. - Both answer the decided attempt and record a
test-member-decisionevent with the operator as actor, listed in thetest-membersaudit category.testMembers.historyattempts an operator decided carryoperatorDecision: { kind, actor, at }.
Both need
publishpermission in the test environment, like the other test-member methods; live refuses them. - New
Patch Changes
- Updated dependencies [2ecde12]
- @domino-sdk/relay@1.50.0
1.49.1
Patch Changes
- 3239667: Tracker trigger outcomes always carry a skip code. A skip recorded before Relay stored codes, whose reason matches no rule, now reads with the code
legacy; reading such outcomes no longer fails.TrackerSkipCodeis already an open string, so no client change is needed: showlegacyby itsreason, and retry it if it should count. The CLI's authoring reference names the code. - @domino-sdk/relay@1.49.1
1.49.0
Minor Changes
-
c262df2:
management.attempts.listtakesquestto list one quest's attempts, by its ID, alone or withqandstatus(managedAttemptQuerySchema.quest).domino attempts list --quest <id>does the same from the CLI. -
41a3fe9: Hosted apps go live. Approving a promotion that carries a hosted app build deploys that build as the project's public live app at
https://<project-slug>.domino.page, with live's own management key (DOMINO_ENVIRONMENTislive), app secrets, KV and SQL stores, and schedule signing key. The previous live build serves until the new one switches in; a failed deployment keeps it, and promoting the build again retries. Before the first such promotion the address answers 404 "Nothing is live here yet".domino secrets,domino storageanddomino schedulestake--environment liveto manage the live app's secrets, stores and jobs. Without the flag they use test, as before; they ignoreRELAY_ENVIRONMENTand the environment inrelay.json.- A CLI sign-in session can set and delete app secrets in live. Other live writes still need Console or an API key.
management.liveApp.get()returns the live app's address and state (none,deploying,liveorfailed, with the failure). Console shows it as the Live app card in Deployments.- The
hosted-live-appcapability is now supported.
Patch Changes
- Updated dependencies [c262df2]
- Updated dependencies [41a3fe9]
- @domino-sdk/relay@1.49.0
1.48.0
Not published to npm. Its changes first reached npm in 1.49.0.
Minor Changes
- 3213a87: Add group membership quest rewards.
groupMembership(group)from@domino-sdk/relay/authoringtakes adefineGrouphandle and adds the member to that group when the completion's rewards are delivered, recording the completion as the reason in the audit history and checking prerequisite-only automatic quests. Publishing refuses a reward for a group that does not exist or is archived. Revoking the completion removes the membership unless an operator's add or another unrevoked completion also backs it;CompletionRevocation.groupsreports each group withstillMember. Archiving a group that a quest rewards fails withgroup-in-use, naming the quests. SDK 1.x keeps the existingkind: "tier"reward envelope with an optionalreward.groupkey (groupRewardandgroupRewardSchemain@domino-sdk/relay/schema), and quest views add an optionalreward.groupwith the group's key and title.
Patch Changes
- Updated dependencies [3213a87]
- @domino-sdk/relay@1.48.0
1.47.0
Not published to npm. Its changes first reached npm in 1.49.0.
Minor Changes
-
7db52a2: Reward X posts only for approved creators. X does not allow apps that reward users for posting, so a deployment that publishes a quest rewarding X posts without an
inGroupprerequisite now returns the warningx-post-rewards-ungatedinwarnings, naming the quests by key. It applies to an X tracker trigger that pays the post's author and to a content quest on an X tracker, when the quest declares rewards or an engagement rule credits it;beneficiary: "inReplyTo"quests are not affected. The deployment still publishes,domino deployprints the warning, and Console shows it on the deployment with a link to Groups. Addrequires: [inGroup(group)]with a group of approved creators you manage in Console Groups, withdomino groups, or withmanagement.groups.The posting-streak and original-post SDK examples, the tracker guides and the CLI skill references now gate X post rewards on an
approved-creatorsgroup.
Patch Changes
- Updated dependencies [7db52a2]
- @domino-sdk/relay@1.47.0
1.46.0
Minor Changes
-
fc0fc19: One deployment holds up to 1,000 quests and 200 quest types (was 50 each).
projectDeploymentSchema.releases,batchReleaseSchema.releases,questDraftChangesSchemaquests and collections, andcatalogDeploymentSchemacollections accept up to 1,000; the limits are exported asquestScaleLimits.domino build,deployanddevaccept up to 1,000 quests and 200 quest types in one manifest. Other resource kinds keep their limits.SDK clients older than this release check drafts against 50 quests and 50 collections, so they cannot read a draft that holds more. Update the SDK before building drafts larger than that.
Patch Changes
- Updated dependencies [fc0fc19]
- @domino-sdk/relay@1.46.0
1.45.0
Minor Changes
- 8bb0a7b: Quest and quest type bundles import the SDK instead of carrying it. The SDK package ships its runtime (
dist/runtime), andbuildQuest(entry, { runtime: true })from@domino-sdk/relay/buildleaves@domino-sdk/relay/authoring,@domino-sdk/relay/schemaand, at the matching version,zodout of the bundle, returning theruntimethe code imports.domino build,deploy,devand hosted builds use it: releases and types record aruntimereference and the deployment carries each runtime once inruntimes. A quest bundle shrinks from about 170 KB to a few kilobytes. Apps on an SDK without a runtime keep building self-contained bundles. A Relay that predates runtimes refuses such a deployment, and the CLI says to update it. See Shared SDK runtime.
Patch Changes
- Updated dependencies [8bb0a7b]
- @domino-sdk/relay@1.45.0
1.44.0
Patch Changes
- f114841:
management.audit.listacceptscategory: "test-members", which lists operators acting as test members in test: creating, submitting a quest as, advancing the clock of, giving and removing wallets for, sending synthetic records as, signing a staging app in and out as, and resetting a test member. Listing every category now includes these records too. The newauditListQuerySchemaexport validates this query andAuditQueryis its input type;auditQuerySchemakeeps the three earlier categories and is deprecated in its favor. Console's Audit history shows these records under a new Test members filter. - f114841:
management.testMembers.resetanddomino test-members resetnow unlink the accounts a test member connected in your app while a staging site acted as it: X, Discord, Telegram, Google and Apple accounts and wallets. Before, such an account made reset refuse withtest-member-reset-blocked. Only the member's link is removed; the account itself is untouched and anyone can link it afterwards. Reset still refuses a member with an account that signs in, an app user ID, or an X account a member import attached. See Reset and create another member. - Updated dependencies [f114841]
- Updated dependencies [f114841]
- @domino-sdk/relay@1.44.0
1.43.0
Minor Changes
-
64e8df3: Test members can post on X, send Discord messages and add Discord reactions, so quests on managed X and Discord trackers can be tried in test without real accounts.
- New
management.testMembers.record(member, { actionId, tracker, data?, as? })writes one synthetic record into a managed X or Discord tracker.datauses the stored record's field paths (contentandlikeCountfor X,channelIdandtextfor messages,emojifor reactions), so it exercises quests'wherefilters; Relay sets IDs and real time.as: "messageAuthor"makes the member the reacted-to message's author. The answer is{ tracker, record, account, stored, admissions }. From the CLI,domino test-members record MEMBER --tracker TRACKER [--data JSON] [--as author|messageAuthor]. - The first record for X or Discord links the test member a synthetic account of that kind, which attribution, member
identitiesandx.connection()read like a real link. Reset removes it with the member's synthetic records. - Tracker record reads mark these records
synthetic: true. Collected and ingested records never carry it. - Console's Try tab sends posts, messages and reactions for X and Discord quests, starting from values that match the quest's filter, and the tracker's record list marks synthetic records.
It needs
publishpermission in the test environment, like the other test-member methods; live refuses it. - New
Patch Changes
- Updated dependencies [64e8df3]
- @domino-sdk/relay@1.43.0
1.42.0
Minor Changes
- b7156c7: Releases and quest types can reference a shared SDK runtime instead of bundling the SDK.
releaseSchema,publishedReleaseSchema, quest type versions and catalog deployment types take an optionalruntimereference ({ digest, sdkVersion }), and project deployments takeruntimes, each runtime's modules sent once. The new@domino-sdk/relay/schemaexports aresdkRuntimeSchema,sdkRuntimeReferenceSchema,sdkRuntimeModulesSchema,sdkRuntimeModulePathSchema,sdkRuntimeLimitsandsdkRuntimeDigest. Existing self-contained bundles work unchanged.
Patch Changes
- Updated dependencies [b7156c7]
- @domino-sdk/relay@1.42.0
1.41.0
Not published to npm. Its changes first reached npm in 1.42.0.
Patch Changes
- Updated dependencies [c8e46ee]
- @domino-sdk/relay@1.41.0
1.40.0
Not published to npm. Its changes first reached npm in 1.42.0.
Minor Changes
-
68bf9eb: Promote test to live as one reviewed request (ADR 0017). A promotion makes live match test for every definition, code-defined and Console-authored, matched by authoring key, and carries the hosted app's active staged build. It lists environment bindings live lacks (integrations, webhooks, participant sign-in, app secrets) as warnings and never copies them.
management.promotions.prepare()(test),history(),get(id),approve(id)andreject(id, { reason })(live, Console session), androllback(id)(live):POST /promotions,GET /promotions/history,GET /promotions/:promotion, andPOST /promotions/:promotion/approve,/rejectand/rollback.domino promote --jsonprepares one from test;domino promotions listanddomino promotions getread them.- New error codes:
promotion-empty(live already matches),promotion-changed(test or live changed since the promotion was prepared; approval changed nothing) andpromotion-decided.
management.deployments.promote(id)anddomino deploy --promotekeep their 1.x request and response shapes. Once the test deployment is published they now prepare a promotion of all of test, answered as a pending deployment record with no resources whose first warning, codeenvironment-promotion, names the promotion. Approve it as a promotion:deployments.getanddeployments.publishdo not accept its ID.Live now refuses direct code deployments.
domino deploy --environment live, with or without--preview, andmanagement.deployments.previewwith a client for live fail with 422promotion-requiredbefore the bundle is checked, whatever the credential; the CLI reports it asENVIRONMENT_UNSUPPORTED(exit 1) and no longer submits anything. Automation that deployed to live should deploy to test, then rundomino promote(ormanagement.promotions.prepare()) and have a person approve the promotion in Console. Live deployments submitted before this change can still be submitted, approved, rejected and rolled back. Request and response shapes are unchanged, so like the other live refusals this ships in a minor release.
Patch Changes
- Updated dependencies [68bf9eb]
- @domino-sdk/relay@1.40.0
1.39.1
Patch Changes
- 08f24a3: An operator who signed in to a project's staging app through Console and holds
publishcan now sign that app in as a test member, through the staging host's/__domino/toolbar/*endpoints. The test member gets an ordinary 8-hour participant session. Issuing and ending it is audited as the operator, and that audit survives reset. Reset now ends a test member's participant sessions instead of refusing. Live, members that aren't test members, password staging sessions, API keys, hosted keys and CLI sessions can't start one. A password staging session gets the new refusal codeoperator-required, which the CLI skill's error reference now lists. - Updated dependencies [08f24a3]
- @domino-sdk/relay@1.39.1
1.39.0
Patch Changes
- Updated dependencies [a9b11a8]
- @domino-sdk/relay@1.39.0
1.38.1
Patch Changes
- 9b02ade:
questConfigurationfrom@domino-sdk/relay/serveraccepts any release-shaped value withconfiguration,fieldsandvalues, so callers no longer need the release's code to read its configuration. - Updated dependencies [9b02ade]
- @domino-sdk/relay@1.38.1
1.38.0
Minor Changes
- 167a840: A quest can make a member wait before trying again after a rejection. Set
retryCooldownMs(1,000 to 604,800,000) on a quest members submit, or Retry cooldown on a quest made in Console, where a new quiz starts at five minutes. After the evaluator or a reviewer rejects the member's latest attempt, a new attempt is refused with 409retry-cooldownuntilretryAt(alsoRelayError.retryAt). Quest progress andQuestViewcarryretryAtwhile the cooldown runs;questStatusreportsretry-cooldown,canSubmitis false until then,isCoolingDown(view)is new, anduseQuestrenders again when it ends. Catalog quest instances take an optionalretryCooldownMs(nullfor none). A quest without the field keeps today's behavior: no cooldown.
Patch Changes
- Updated dependencies [167a840]
- @domino-sdk/relay@1.38.0
1.37.1
Patch Changes
- 2f18822: Hosted apps moved to
domino.page. New staged builds serve athttps://<project-slug>--staging.domino.page, anddomino devprintshttps://<project-slug>--<workspace-hash>.domino.page.domino capabilitiesdescribes the new staging address. The CLI shows whatever host the API returns, so no upgrade is needed. Staging links onstaging-<project-slug>.domino.runfrom earlier builds keep working; stage again to get the new address. - @domino-sdk/relay@1.37.1
1.37.0
Not published to npm. Its changes first reached npm in 1.37.1.
Minor Changes
- 701a0d8: Leaderboards can list every member.
defineLeaderboard({ includeUnscored: true })adds the board's eligible members who have no qualifying points to its standings,me,nearbyandplacementreads, with a score of 0. They share one rank after every member who scored and are ordered by member ID; the board's group and exclusion group still apply. Settlements leave them out, so they win no prizes. Boards without the option rank as before. - e1b3c70: A points reward can pay up to 2,147,483,647 points, the largest 32-bit signed integer, instead of 10,000. The new limit applies wherever
points()andpointsRewardSchemaare used: quest rewards, evaluator decisions, leaderboard prizes and referral rewards. Offer prices (offerPriceSchema) take the same maximum and still start at 1. Code that declares a larger amount needs this SDK version, and apps that read one need SDK 1.10.0 or later, whose response readers ignore these maximums. - ff024c7: Scheduled jobs can report what a run did. A job route answers with
scheduleResult({ outcome, summary?, metrics?, diagnostics? })from@domino-sdk/relay/server, and Relay records it with the run asresult. Anoutcomeofpartialorfailedrecords the run as failed even when the HTTP status is 2xx. Relay reads anyapplication/jsonanswer up to 16,384 bytes. An invalid or larger one leaves the status alone and sets the run'serrortoResult ignored: …. Each schedule keeps its latest 20 runs:management.schedules.runs(name)(GET /schedules/:name/runs,domino schedules runs NAME, Console Run history) returns them newest first, and needsreadpermission. - ff024c7: Read a hosted app's SQL value in full where a rows page cuts it at 2,000 characters.
management.storage.cell(name, table, { row, column, cursor? })(GET /storage/sql/:name/cell?table=&row=&column=,domino storage cell DB TABLE ROW COLUMN [--all]) returns up to 100,000 characters per page with the value'stypeandlength, and anextCursorfor the rest;--allfollows it.rowis the row's position in storage order, the rows page'soffsetplus its index on the page. A BLOB answers with its size and no text. It needspublishpermission, like the other storage reads. Console's row view has Show full value.
Patch Changes
- Updated dependencies [701a0d8]
- Updated dependencies [e1b3c70]
- Updated dependencies [ff024c7]
- Updated dependencies [ff024c7]
- @domino-sdk/relay@1.37.0
1.36.0
Patch Changes
- 5da9393: Participants read their own email address.
relay.me.get()(GET /v1/me) anduseMe()now returnemails, the verified addresses that sign the member in (each email-code address, then the verified email of each Google or Apple account enabled for sign-in), andemail, the first of them for "signed in as". A member who signs in only with a wallet, X, Discord, Telegram or an app login has none:emailis null andemailsis empty. Participant routes never return another member's email. The classic starter shows the address in its account menu and Settings page. - Updated dependencies [5da9393]
- @domino-sdk/relay@1.36.0
1.35.0
Not published to npm. Its changes first reached npm in 1.36.0.
Minor Changes
-
f7ba8d6: Create and change X search trackers without a deployment.
management.trackers.create({ key, title, query, window, refreshSeconds?, maxPages? })(POST /trackers,domino trackers create) creates one as data in test, with the same validation asdefineTracker; its first collection searches fromwindow.start.management.trackers.update(tracker, changes)(POST /trackers/:tracker/update,domino trackers update) changes any field but the key, and a new query or window never searches the past. Both needpublishpermission, and live refuses them withpromotion-required: a data tracker reaches live through a promotion from test, which is not available yet. Console Trackers creates and edits them too.A key belongs to code or to data. Tracker status reports it as
origin(codeordata). A deployment that declares a data tracker's key is refused with 409tracker-owned-by-data, and creating or updating a key a deployment publishes with 409tracker-owned-by-code.
Patch Changes
- Updated dependencies [f7ba8d6]
- @domino-sdk/relay@1.35.0
1.34.0
Minor Changes
-
81d0037: Definitions change in test and reach live only through a promotion (ADR 0017). In live, every definition write fails with 422
promotion-required, whatever the credential, Console sessions included. These 1.x management routes refuse live:POST /releases,/releases/batch,/releases/settings,/releases/lifecycle, and/quests/:quest/archiveand/unarchivePOST /catalog/deploy,/catalog/quests,/catalog/draftsand/catalog/publishPOST /engagement/publishand/engagement/changePOST /point-ledgers,/point-ledgers/update,/point-ledgers/:ledger/update,/archiveand/unarchivePOST /calendarand/profile-settingsPOST /badges,/badges/:badge/update,/archiveand/unarchivePOST /pickup-inventoryand/leaderboards/:board/exclusion-groupPOST /stock-items,/stock-items/:item/rename,/archiveand/unarchivePOST /resources/lifecycle, and/archiveand/unarchiveunder/trackers,/referral-programs,/leaderboards,/engagement/rules,/collections,/quest-typesand/images
API keys used to get 403
console-approval-requiredon most of these. Pickup policy, exclusion-group bindings and stock item creation, renames and archive state accepted API keys in live. Operations keep working in live under the caller's permissions: pauses, points, groups, moderation, reviews, entitlements, redemptions and stock adjustments. Code deployments still reach live through deployment review in Console.This ships as a minor release as an accepted exception to the compatibility policy: no request or response shape changes, the refusal is one new error code, and no project had written a definition to live in production. The CLI reports the refusal as
ENVIRONMENT_UNSUPPORTEDwithapiCode: "promotion-required".
Patch Changes
- Updated dependencies [81d0037]
- @domino-sdk/relay@1.34.0
1.33.0
Minor Changes
- 3706733: Add member imports: bring members, opening point balances and quest progress from another platform into a project and environment.
domino import --file members.jsonl --source NAME --dry-runuploads a JSONL file, checks every row and prints what applying would create, link, set and refuse; without--dry-runit applies the import in test.domino imports list|get|rows|apply|cancelandmanagement.memberImports.create|addRows|check|apply|cancel|get|rows|listreach the same/management/v1/member-importsroutes. A live import is applied only from Console. Imported opening balances areadjustentries that name their import (PointsEntry.import, andimportedin a member's own point history); they emit nopoints.awardedand count on balance leaderboards only. Imported progress pays no rewards. Accounts an import vouches for carryimported: trueonManagedWallet,ManagedOAuthAccountandManagedMemberIdentity.
Patch Changes
- Updated dependencies [3706733]
- @domino-sdk/relay@1.33.0
1.32.0
Minor Changes
-
d6ea67b: Service keys give a self-hosted app's backend a management credential that no person owns. An organization admin creates one in Console under Settings → API keys for one project and environment, with chosen permissions. It never expires unless an expiry is chosen, and it keeps every live fence a personal key has.
management.serviceKeyslists, creates and revokes them from an admin's Console session. Rotation overlaps:addSecretissues a second secret while the first keeps working, andrevokeSecretretires the old one. Each secret reportslastUsedAt.Identity integration keys follow the same policy. New keys never expire unless
expiresInDaysis given, and existing keys keep their expiry until an admin removes it withidentityIntegrations.removeSecretExpiry.identityIntegrations.addSecretandrevokeSecretrotate a key without signing members out. Integrations now list theirsecrets.expiresAtis deprecated: it reports the latest secret expiry, or 8640000000000000 when a secret never expires.identityIntegrations.rotateis deprecated too. It now replaces every secret with one that never expires, instead of extending the key by 90 days.
Patch Changes
- Updated dependencies [d6ea67b]
- @domino-sdk/relay@1.32.0
1.31.0
Minor Changes
-
07802d2: Reward deliveries, redemptions and deployments page by
cursorandnextCursor, so long histories no longer stop silently.management.rewardDeliveries({ member, cursor, limit })andrelay.me.rewardDeliveries({ cursor, limit })answernextCursorbesidedeliveries. A page holds up to 500 deliveries, 500 by default, so calls without a cursor read what they did before.management.rewardDeliveries(member)still works.- New
management.redemptions.history({ member, cursor, limit })answers{ items, nextCursor }, 25 a page by default and at most 100, and replacesmanagement.offers.redemptions, which answers every redemption at once. From the CLI,domino redemptions history [--member] [--cursor] [--limit]replacesdomino redemptions list. - New
management.deployments.history({ cursor, limit })answers{ items, published, nextCursor }, 25 a page by default and at most 100.publishedholds the two newest published deployments by revision on every page: the live one and the onerollbackreturns to. It replacesmanagement.deployments.list, which stops at the 100 newest.
The deprecated forms keep working through 1.x and are removed in 2.0.0.
Patch Changes
- caf698a: To prepare project slugs for hosts on
domino.page, the server now refuses a slug change to a name Domino reserves (409Project slug is reserved) or to a slug shorter than 3 characters (400), and a new project's derived slug gets a short suffix instead of taking either. Reserved names match exactly:consoleis reserved,console-toolsis not.projectSchemastill parses every existing slug; itsslugfield documents the rule, and the agent references anddomino capabilitiesdescribe it. - Updated dependencies [caf698a]
- Updated dependencies [07802d2]
- @domino-sdk/relay@1.31.0
1.30.1
Patch Changes
- bb09650: The participant API reference's
useRelayMutationand server-rendereduseRelayQueryexamples are now complete components that compile, instead of fragments. - @domino-sdk/relay@1.30.1
1.30.0
Minor Changes
- cd6d78c: Add
management.x.lookup({ username })anddomino x lookup HANDLE, which resolve an X handle (with or without@) to the account that holds it,{ id, username, name }(read through a cache up to a day old), fromGET /management/v1/x/users/lookup. Trackers,--authorfilters, customxidentities andctx.x.follows({ userId })take the stableid, which no longer has to be copied from a post in Preview X search. The lookup needsreadpermission, fails with 404not-foundwhen no account holds the handle, and allows 30 lookups a minute per user and 300 an hour per organization. It resolves the handle only: it does not show who owns the account.
Patch Changes
- Updated dependencies [cd6d78c]
- @domino-sdk/relay@1.30.0
1.29.0
Minor Changes
- 272a255: Add
management.members.progress(member)anddomino members progress MEMBER, which read a member's progress on each active quest, 25 most recent attempts and point balances fromGET /management/v1/members/:member/progress. They leave out the quest releases and evaluator code thatmembers.getreturns beside them. In a project with many quests, member detail runs to megabytes; every list in this read is bounded by the project's configuration, so it suits scheduled jobs and other reads under a deadline.members.getis unchanged.
Patch Changes
- Updated dependencies [272a255]
- @domino-sdk/relay@1.29.0
1.28.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
932e3c1: Add kind-keyed operator identity commands.
management.members.identityStates.list(member)reads every identity a member proved (wallets, X, Discord and Telegram accounts, andapp:<name>identities) with its link, revision, sign-in and allowed actions.identityStates.unlinkandrelinktake anidentityselector, anexpectedRevisionand a reason, andidentityStates.setSignInturns a wallet's sign-in on or off on its own. Each kind keeps its rules: wallet and account unlinks keep sign-in, app identity unlinks end that identity's sessions, and a Telegram account can now be relinked. The CLI addsdomino members identities,unlink-identity,relink-identityandidentity-sign-in.Add
management.discordRoles.preserve(delivery, { reason }), the Discord role reward kind's preservation operation. A delivery of a reward kind without that operation is refused with 409.Deprecated, still supported through 1.x:
members.oauthAccounts,unlinkOAuthAccount,telegramAccount,unlinkTelegramAccount,unlinkWallet,setWalletSignIn,unlinkIdentity,relinkIdentityandidentityLinks(usemembers.identityStates),management.preserveRewardRole(usemanagement.discordRoles.preserve), and the CLI'smembers identity-links,members relinkandmembers oauth-accounts.
Patch Changes
- Updated dependencies [932e3c1]
- @domino-sdk/relay@1.28.0
1.27.1
Not published to npm. Its changes first reached npm in 1.29.0.
Patch Changes
- 4746d5a: Corrected the agent references:
domino capabilitiesnow describes X account linking (OAuth with PKCE, stable X user ID) and profile badge reads, and the participant API reference says apps using their own login can still connect X and Discord accounts and link wallets. - @domino-sdk/relay@1.27.1
1.27.0
Not published to npm. Its changes first reached npm in 1.29.0.
Minor Changes
-
072493d: Leaderboard exclusions are a group (DOM-205):
- A board leaves out the members of its exclusion group. Each board gets its own group, keyed
domino--exclusions--<board ID>, on its first exclusion. Existing exclusions and their history move into these groups, so standings and settlements leave out the same members at every cutoff. management.leaderboards.exclusionGroup(board)returns{ leaderboard, group, generated, boundAt }, andmanagement.leaderboards.bindExclusionGroup(board, { group, reason })binds the board to another unarchived group, such as one several boards share, or back to its own. Settlements read the group bound at their cutoff.management.leaderboards.excludeandexclusionskeep their inputs, responses, permissions and audit events. They now change and read the board's exclusion group, whichmanagement.groupscan change too.- Archiving a group that a leaderboard uses as its exclusion group fails with 409
group-in-use, naming the boards inleaderboards. Group keys starting withdomino--exclusions--are reserved: creating one fails with 409already-exists. - CLI:
domino leaderboards exclusion-group BOARDanddomino leaderboards bind-exclusion-group BOARD GROUP --reason ….
- A board leaves out the members of its exclusion group. Each board gets its own group, keyed
Patch Changes
- Updated dependencies [072493d]
- @domino-sdk/relay@1.27.0
1.26.0
Not published to npm. Its changes first reached npm in 1.29.0.
Patch Changes
-
91d7ad0: Relay keeps quest submissions (DOM-268). See ADR 0014.
- Submission handles (
me.submission) and quest controllers (quests.open) save each submission in Relay instead of the browser. A lost answer, a reload, another tab or another device signed in as the same member recovers it from Relay, and submitting again returns the same attempt. The public API is unchanged. Submissions no longer need IndexedDB or Web Locks, socreateRelayClientwithout asubmissionStoresubmits too.SubmissionStoreandbrowserSubmissionStoreare used only to finish a submission an older SDK version saved in the browser. - A member has at most one unfinished attempt per quest. Another claim, or a retry of a failed attempt, is refused with
attempt-pendinguntil the previous one is decided, including while it waits for human review. The same action sent twice still replays. - New participant routes:
GET/POST /v1/me/quests/:quest/submission,GET/POST /v1/me/submissions/:submission, andPOST /v1/me/submissions/:submission/submit,/discardand/evidence(a photo uploaded into the submission). A submit refused because the quest changed keeps the draft withreviewRequired, and the handle reportsneeds-review.@domino-sdk/relay/schemaexportsquestSubmissionSchema,questSubmissionUpdateSchema,questSubmissionResultSchemaandQuestSubmissionStatus. A draft expires 7 days after its last change. Refusal codesubmission-closedmeans the submission was already submitted, discarded or expired. - The management client has
quests.submissions(member)andquests.discardSubmission(member, submission). Console shows a member's open quest submissions and lets reviewers and staff discard one.
- Submission handles (
-
Updated dependencies [91d7ad0]
- @domino-sdk/relay@1.26.0
1.25.1
Not published to npm. Its changes first reached npm in 1.29.0.
Patch Changes
- 07b7bc1:
management.attempts.confirm(POST /management/v1/attempts) andmanagement.passes.verify(POST /management/v1/passes/verify) now need a member that already exists in the project's environment. An unknown member ID is refused with 404not-foundand creates nothing; before, Relay created a member with no sign-in identity, who then collected the quest's rewards. Members who exist but have not joined the project still join it, and test members keep working. The CLI's skill references describe the rule. - @domino-sdk/relay@1.25.1
1.25.0
Not published to npm. Its changes first reached npm in 1.29.0.
Patch Changes
-
55d08ae: Client SDK paper cuts (DOM-160):
createRelayClienttakesgetToken: async () => token | null, read before every attempt, as the management client does. A failinggetTokenrejects with codecredentialbefore anything is sent.tokenandgetTokentogether are refused.createRelayClientandcreateManagementClienttakefetch, which replaces the globalfetchfor every call, andheaders, added to every call. The client's own headers win over one of the same name; an invalid header is refused withinvalid-inputwhen the client is created.- Submission handles (
me.submission) and quest controllers (quests.open) take{ signal?, timeout? }onsubmit,resume,statusanddiscard. Each request the call sends (session read, upload, claim, retry, and a controller's read of the latest quest afterquest-updated) gets the signal and what is left of one deadline for the whole call. A cancelled or timed-out call keeps its saved progress forresume(). The quiz'ssubmit(answers, { release?, signal?, timeout? })makesreleaseoptional. auth.start({ provider, mode })andauth.emailVerify({ challenge, code })take an options object, which refuses unknown fields. The positional forms still work and are deprecated, to be removed in 2.0.0.paginate(read)from@domino-sdk/relayiterates every item of a paged read withfor await, followingnextCursor.RelayErrorCodelistsOperationRetired(operation-retired) andCredential(credential).offers.redeem,profiles.badges,me.activity.list,me.upload,auth.telegram.miniApp.connectandauth.emailStartreject invalid input with aRelayErrorof codeinvalid-input, instead of throwing a rawZodError(offers.redeem) or throwing synchronously.- The browser bundle no longer includes every Zod locale:
createBrowserRelayClientdrops from about 412 KB to 198 KB minified (90 KB to 56 KB gzipped).
-
Updated dependencies [55d08ae]
- @domino-sdk/relay@1.25.0
1.24.1
Patch Changes
- 2fd54a5:
domino referencenow serves every reference the skill ships, includingexisting-app,hosted,webhooksanderrors, so each capability'sreferenceresolves. The references now say the CLI sends a request at most three times in all, and that the 50 app-secret limit applies to each project environment. Capability entries explain why referral milestones and external qualification are not compositions. - @domino-sdk/relay@1.24.1
1.24.0
Minor Changes
-
f6e030a: Webhook endpoint creation and secret rotation can be recovered after a lost answer.
management.webhooks.createtakes an optionalactionId(newwebhookCreateInputSchema), andmanagement.webhooks.rotateSecret(id, { actionId })takes one too (newwebhookRotateInputSchema). The client generates an ID when you omit it. A retry with the sameactionIdreturns the same endpoint and signing secret instead of creating a second endpoint, or being refused during the 24-hour overlap. Once the secret is rotated again, the earlier action's retry is refused withsecret-superseded.domino webhooks createanddomino webhooks rotate-secretare now saved operations with--operation-id; resume one withdomino operations resume <id>.Relay now refuses webhook subscriptions to event types it never sends, such as
completion.create, withunknown-activity-type; the message lists the valid types. This narrows what the API accepts, but the CLI and SDK already report it as a refusal. An endpoint update can keep a type the endpoint subscribed to before. Event payloadtypestays an open string, so receivers must still accept or ignore types they do not know.
Patch Changes
- Updated dependencies [f6e030a]
- @domino-sdk/relay@1.24.0
1.23.2
Patch Changes
- f049af8: Agent references and capabilities now say that Discord and Telegram trackers keep activity only for accounts a member of the tracker's own project linked, now that members belong to one project.
- @domino-sdk/relay@1.23.2
1.23.1
Patch Changes
- fd5f211: Members now belong to one project. A person who signs in to two projects of the same organization is a separate member in each, with their own identities, linked X, Discord and Telegram accounts, and wallets; unlinking, relinking and wallet sign-in control act within the project. Project transfer now moves a project with its participants instead of refusing it. No API shape changes:
MemberIdentityLink.scopekeeps the value"organization-environment"for OAuth links, which now names a link in the member's project and environment. - Updated dependencies [fd5f211]
- @domino-sdk/relay@1.23.1
1.23.0
Minor Changes
-
88c7c00: Add Telegram chats, membership checks and reaction trackers. Operators connect Telegram groups and channels to a project through Domino's bot in Console (Integrations → Telegram → Add chat), or with
management.telegram.connect(),chats()andremove(chat). See Telegram.ctx.telegram.member({ chat })reads whether the member's linked Telegram account is in a connected chat:{ userId, username, status, isMember, isAdmin, untilDate }, or null. It needs nointegrationsentry.ctx.telegramis optional inEvaluationContext, so code that builds contexts keeps compiling.settings.telegramChat()adds a setting that Console fills from the connected chats. The testing kit takesfacts.telegram.member.telegram.reactions()is a tracker source of the reactions members add in connected groups. Each record carries Telegram accounts for the reactor (author) and the message's author (messageAuthor).dataisTelegramReactionData, which includesreactorStatusandreactorIsAdminfor a trigger'swhere. In SDK 1.x it is a custom source on the reserved namespacetelegram-reactions, which Relay alone writes.- Text settings can carry
picker, which Console uses to offer a list. - The
telegram-membershipsnippet is a join quest.
Patch Changes
- Updated dependencies [88c7c00]
- @domino-sdk/relay@1.23.0
1.22.0
Minor Changes
- 81beec3: Personal API keys and
domino loginCLI sessions can now use test members, including giving them wallet addresses, not only Console sessions: the caller needspublishpermission in the test environment, and hosted app keys stay refused. Adddomino test-members create|list|history|act|advance|link-wallet|unlink-wallet|reset, which always address the test environment and save their writes as resumable operations, so a coding agent can simulate a campaign end to end.
Patch Changes
- 07f3a99: Add the
campaign-testingagent reference (domino reference campaign-testing): how to simulate a campaign over several days with test members, read the results, fix and replay, and what to watch for, such as odd characters, thresholds, missing provider fields, day boundaries, referral chains and balance gates. - Updated dependencies [81beec3]
- @domino-sdk/relay@1.22.0
1.21.0
Minor Changes
- 5db81c1: Test members can hold wallet addresses.
management.testMembers.linkWallet(member, { actionId, chain, address, network? })gives a test member a Solana, EVM or Cosmos address without a signature, andunlinkWallet(member, { actionId, chain, address })removes and frees one. Quest code reads these addresses throughctx.walletlike a member's linked wallets, so balance-gated quests can be tried in the test environment. When the wallet's owner signs in with it or connects it, the address moves to them. Test member results include an optionalwalletslist. The CLI's capability map and Domino skill reference describe the new methods.
Patch Changes
- Updated dependencies [5db81c1]
- @domino-sdk/relay@1.21.0
1.20.0
Minor Changes
-
2e1eff4: Add Telegram sign-in. Telegram Login signs participants in on the web where Domino configures it, and Mini App sign-in signs them in inside a project's own Telegram Mini App once the project names its bot ID in Console (Settings → Sign-in). Both reach one Telegram account. See Telegram.
Telegram is not an
AuthProviderin SDK 1.x, so code that switches over providers keeps working:AuthProvider,relay.auth.accounts(),useAccounts()anduseSignInOptions().optionsnever include it. Telegram has its own additions beside them:relay.auth.providers()returnstelegram: { login, miniApp }.relay.auth.telegramhasstart(mode),account(),disconnect(), andminiApp.signIn(initData)andminiApp.connect(initData).telegramMiniAppInitData()reads the Mini App's launch data.useSignInOptions()andsignInOptions()return the Telegram Login option astelegram, besideoptions.useSignIn()addswithTelegram()andwithTelegramMiniApp(initData), anduseTelegramAccount()connects and disconnects the member's Telegram account.- In the management client,
participantSignIn()returnstelegram: { login, miniAppBotId }, andconfigureParticipantSignIn()takestelegram: { miniAppBotId }, which keeps the current bot when left out.members.telegramAccount(member)andmembers.unlinkTelegramAccount(member, { subject, revision, reason })read and unlink a member's Telegram account.
Patch Changes
- Updated dependencies [2e1eff4]
- @domino-sdk/relay@1.20.0
1.19.0
Minor Changes
- 37a57df: Deploying a quest that needs an unconnected integration, such as Discord, no longer fails. The deployment preview and result include
warnings(deploymentWarningSchema) naming the quests with codeintegration-required, anddomino deployprints them. Their attempts still fail withintegration-requireduntil the integration is connected.
Patch Changes
- Updated dependencies [37a57df]
- @domino-sdk/relay@1.19.0
1.18.1
Patch Changes
- 0a6ebf1: Allow management deployment responses to include additional fields in direct, promotion and rollback sources. This closes the recorded response compatibility gap without changing known fields or source kinds.
- Updated dependencies [0a6ebf1]
- @domino-sdk/relay@1.18.1
1.18.0
Minor Changes
- 7000d41: Add project badge definitions, quest badge rewards, audited manual awards and revokes, participant and profile reads, and React hooks. Author badges with
defineBadge, include them inrelay.json, and attach them withbadge(...). SDK 1.x retains the existing reward envelope with optional badge metadata. The CLI addsdomino badgescatalog and member commands.
Patch Changes
- Updated dependencies [7000d41]
- @domino-sdk/relay@1.18.0
1.17.0
Not published to npm. Its changes first reached npm in 1.18.0.
Minor Changes
- d8a5731: Add ordered quest presentation steps with HTTPS link actions, images and alt text, and account or wallet hints. Collection declarations now accept description, image and alt text. Participant reads and React hooks return these optional fields in the published collection order. Console can edit and reset quest presentation and edit collection details. Presentation changes do not change verification.
Patch Changes
- Updated dependencies [d8a5731]
- @domino-sdk/relay@1.17.0
1.16.0
Minor Changes
-
b9d767f: Run background jobs on active hosted Worker builds in test. Declare
app.schedulesin relay.json with an interval or UTC cron, verify signed route calls withverifyScheduledRequest(request, env.DOMINO_SCHEDULE_KEY), and inspect or control jobs withmanagement.schedulesordomino schedules.Jobs have a five-minute minimum cadence, ten schedules per app and a 30-second timeout. Relay makes one attempt per tick; uncertain completion blocks future runs until an operator confirms recovery. See Run scheduled jobs for custom tracker ingestion and recovery.
Patch Changes
- Updated dependencies [b9d767f]
- @domino-sdk/relay@1.16.0
1.15.1
Not published to npm. Its changes first reached npm in 1.16.0.
Patch Changes
- 54a725e: Document non-retryable Discord configuration failures, their
integration-requiredattempt code, publication prerequisites, and repair-before-retry guidance in the installed agent references. - @domino-sdk/relay@1.15.1
1.15.0
Minor Changes
- e6849ac: Add typed
domino <noun> list|get|…commands for every management primitive, built on the SDK management client:points get|list|award|spend|adjust|set|reverse,point-ledgers list|create|update,trackers list|get|records|summary|content|ingest|outcomes|retry-outcome,leaderboards list|standings|history|exclusions|exclude|include,leaderboard-settlements approve,referral-programs list|qualify,referral-relationships list|history,referral-rewards resolve,offers list,redemptions list|cancel,profiles settings|configure|moderate,calendar get|configure,members get|lookup|oauth-accounts|counters|moderate-counter,groups get|member,quests list|get,releases list|get,attempts list|get,metrics getandpauses list. Each prints a typed result in the CLI contract. Every change exceptpoint-ledgers update, which sets a state, is saved before it is sent and takes--operation-id, which becomes the request'sactionId, so an uncertain answer resumes withdomino operations resume. The existing archive, unarchive, pause, resume, refresh, settlement reject, quest enroll and withdraw, and identity relink commands are saved the same way; their--action-idstill sends the change unsaved. The barestatus,questsandreleasespage reads andgroups showare deprecated in favor of these commands and are removed in 2.0.0;contract.jsonmarks each deprecated command with adeprecatednote. Each capability indomino capabilitiesnow names its commands incli. See resource commands.
Patch Changes
- @domino-sdk/relay@1.15.0
1.14.0
Minor Changes
- 0c394de: Replay captured test-environment attempts locally.
management.attempts.capture(attempt)(publishpermission) returns what one evaluation run of an attempt read: the release's settings and rewards, the attempt's input, frozen clock, seed and calendar, each captured capability answer, and what quest code decided.replayAttempt(quest, capture)from@domino-sdk/relay/testingevaluates current quest code against it and reports the decision beside the recorded one, whether it changed (kind, reason, rewards or completion key), and the first call the capture could not answer.domino quests replay <attempt>fetches, saves (--out) and replays a capture. Captures are served from the test environment only: live attempts are refused with 422test-environment-required, and an attempt that has not been evaluated with 409attempt-not-evaluated.
Patch Changes
- cb252b6: The CLI now sends its management requests through the management client's own transport, so
dominoandcreateManagementClientshare one implementation of deadlines, retries and failure classification. CLI exit codes, error codes and results stay the same; some error messages change wording, and network errors no longer carrydetails.cause. A management client write whose connection was refused, never resolved, could not reach the network or host, timed out while connecting, or failed TLS now fails withuncertain: false, because nothing was sent, and a 204 answer reads asnull. - 3ccb72a:
Personrecords (members.people(), and thepeopleof attempt, entitlement and page reads) now carrytestandnamefor Console test members in the test environment, the same fields member reads already had. Console names test members by that name and marks them as test members instead of showing their member ID. - Updated dependencies [0c394de]
- Updated dependencies [580a641]
- Updated dependencies [cb252b6]
- Updated dependencies [3ccb72a]
- @domino-sdk/relay@1.14.0
1.13.0
Not published to npm. Its changes first reached npm in 1.14.0.
Patch Changes
- Updated dependencies [c3601dd]
- @domino-sdk/relay@1.13.0
1.12.0
Not published to npm. Its changes first reached npm in 1.14.0.
Minor Changes
- 80f823a: Add
@domino-sdk/relay/testing, a kit for unit-testing quests locally.runQuest(quest, { input, tracker, settings, facts, now, seed })evaluates a quest the way Relay's hosted runtime does: its published settings, rewards and review switch, a frozen clock, seeded randomness, fake answers forctx.member,ctx.wallet,ctx.x,ctx.discord,ctx.trackers,ctx.imagesandctx.reviewchecked against Relay's capability contracts, and the reward bounds Relay enforces.trackerObservationandtrackerPagebuild typed tracker input,expectAccept,expectReject,expectReview,expectDeferandexpectProgressassert decisions, andcreateTestContextreturns the host contextquest.evaluatetakes.buildQuestnow refuses a quest bundle that imports the testing kit. Starters include quest unit tests intests/quests.test.ts.
Patch Changes
- Updated dependencies [80f823a]
- @domino-sdk/relay@1.12.0
1.11.2
Not published to npm. Its changes first reached npm in 1.14.0.
Patch Changes
- Updated dependencies [7160db6]
- @domino-sdk/relay@1.11.2
1.11.1
Patch Changes
- Updated dependencies [90e5141]
- @domino-sdk/relay@1.11.1
1.11.0
Minor Changes
- 76e7a45: Add operator quest withdrawal, inventory rename/archive/unarchive/stock correction, webhook deletion with retained history, settlement rejection, and kind-keyed wallet/OAuth relink. Existing 1.x methods and response shapes remain supported. Inventory archive refuses retained offer and pickup references with named blockers; relink applies the current quest's historical policy while preserving admitted attempts.
Patch Changes
- Updated dependencies [76e7a45]
- @domino-sdk/relay@1.11.0
1.10.1
Patch Changes
- Updated dependencies [fa55eff]
- @domino-sdk/relay@1.10.1
1.10.0
Patch Changes
-
f2724f9: Read added response fields at every depth without relaxing request validation. Add explicit supported/unsupported response schemas and
client.response(...)for extensible results. Unsupported entities retain their raw data; entity lists retain positions and report an unsupported count. Existing 1.x method return types stay compatible.SDK and CLI calls identify their package, version and runtime with
X-Domino-Client. Tolerant SDK calls also identify their per-call response capability withX-Domino-Response: tolerant-v1. Older and legacy callers still require compatible server wire shapes. See the participant response reference for the opt-in API. -
Updated dependencies [f2724f9]
- @domino-sdk/relay@1.10.0
1.9.0
Patch Changes
- Updated dependencies [4ae54f9]
- @domino-sdk/relay@1.9.0
1.8.0
Not published to npm. Its changes first reached npm in 1.9.0.
Minor Changes
- f19da0d: Add canonical archive, unarchive, pause and resume methods and CLI commands for management resources. Keep older lifecycle and feature-specific publication forms working as deprecated 1.x APIs.
Patch Changes
- Updated dependencies [f19da0d]
- @domino-sdk/relay@1.8.0
1.7.0
Patch Changes
- Updated dependencies [5662b50]
- @domino-sdk/relay@1.7.0
1.6.0
Minor Changes
-
4651887: Add an optional
windowdetail tooutside-windowavailability, errors, and automatic outcomes so clients can distinguish tracker and quest windows without branching on reason text. -
547600e: Every refusal from the participant and management APIs now carries a machine-readable
codebeside its unchanged message, so clients can branch without matching text.RelayError.codeandManagementError.code(and the CLI'sapiCode) are set for every API answer: a specific code when the client can act on it, such ascatalog-changed,draft-changed,prerequisites-incomplete,permission-requiredornot-configured, or the status's own code otherwise (not-found,conflict,forbidden, …). Structured detail, such as themissingprerequisites, comes with the code on every route. The API error codes reference lists them.RelayErrorCodeaddsPrerequisitesIncomplete,QuestClosed,QuestPaused,ProfileLocked,InvitationNotFound,InvalidCode,CodeExpired,ChallengeExpiredandRateLimited.- New
managementErrorSchema(@domino-sdk/relay/server) parses the management API's error body, for rawapi.raw()answers.
Messages keep their text, and
codestays an open string, so code written against 1.2 keeps working.The participant proxy now codes its own routing, configuration and authentication refusals. During application identity exchange it also keeps Relay's safe upstream code and
Retry-Afterwhile continuing to replace non-account-conflict messages with its existing generic text. -
6c80795: Groups are their own resource. A group is a named set of members that
inGroupprerequisites, a leaderboard'sgroupand a referral program'sinviterGroupread by key; before, groups existed only as leaderboard group memberships. Existing memberships and their history move over unchanged.management.groupslists, gets, creates, renames, archives and unarchives groups, and lists, adds and removes members with a reason for the audit history. Lists take{ cursor, limit }and return{ items, nextCursor }. The schemas andManagedGroup,GroupPage,GroupMemberandGroupMemberPagetypes are exported from@domino-sdk/relay/schemaand@domino-sdk/relay/server.domino groups list|show|create|update|archive|unarchive|members|add|removedoes the same from the CLI.- Groups are archived, never deleted. An archived group's members still count, but cannot change until it is unarchived.
management.leaderboards.group(group, { member, included, reason })andleaderboardGroupSchemaare deprecated and removed in 2.0.0. They keep working, and still create a group the first time they add a member to it. The group name must now be a group key (letters, digits,_and-, at most 100 characters), as definitions already required; a longer name answers 400. Replace them withmanagement.groups.create({ key, title })once, thenmanagement.groups.addMember(group, { member, reason })andmanagement.groups.removeMember(group, member, { reason }).
defineGroupandinGroupare unchanged. See groups. -
6088922: Every paged management read now takes
cursorand answersnextCursor, as participant reads already did: pass a page'snextCursorback ascursoruntil it isnull.management.points.history(member, ledger, { cursor })andmanagement.referrals.participants({ cursor })take an options object; their answers addnextCursor. Passing the numericbefore(a page'snext) still works and is deprecated.management.webhooks.deliveries(id, { cursor, limit })anddomino webhooks deliveries <endpoint> --cursor <cursor>page by the answer'snextCursor.before,--beforeandnextare deprecated.management.storage.rows(name, table, { cursor })anddomino storage rows NAME TABLE --cursor Cpage bynextCursor;offsetand--offsetare deprecated.management.storage.keysanswersnextCursor; itscursorfield is deprecated.- New
management.trackers.triggerOutcomes(tracker, { ...filters, cursor })answers{ items, nextCursor }and replacesmanagement.trackers.outcomes, whosebeforeandnextstay as they were. - New
management.engagement.ruleVersions(rule, { cursor })answers{ items, nextCursor }and replacesmanagement.engagement.versions({ rule, before }). resourceOptionsSchema(@domino-sdk/relay/schema) addsnextCursor; itscursorfield is deprecated.
The deprecated forms keep working through 1.x and are removed in 2.0.0. Responses gain only fields, so code written against 1.2 keeps compiling.
-
6fa9cd5: New
management.members.list({ q, needsReview, limit, cursor })(GET /management/v1/members/list) anddomino members list [--q TEXT] [--needs-review] [--limit N] [--cursor C]list a project's members, most recently joined first, with their accounts and attempt, completion and reward counts. The answer is{ items, total, nextCursor }: passnextCursorback ascursoruntil it isnull.qfinds members by part of their ID, profile name, or an account's subject, name or email, as Console's member search does. Use it instead ofpage("members", { q }).memberListSchema,memberPageSchemaandMemberPage(@domino-sdk/relay/schema), which no route used, are deprecated in favour ofmanagedMemberQuerySchema,managedMemberPageSchemaandManagedMemberPage(@domino-sdk/relay/server), and are removed in 2.0.0. -
e3a050e: Management commands on an existing resource now name it in the path, and the client methods take it as their first argument:
management.deployments.publish(id),submit(id),reject(id, { reason }),promote(id, { actionId? })androllback(id, { actionId? })call/deployments/:deployment/<verb>. The{ id, ... }forms still work and are deprecated.domino deployuses the new routes.management.pointLedgers.update(ledger, { name, description })calls/point-ledgers/:ledger/updateand replaces the deprecatedpointLedgers.save({ id, name, description }).management.referrals.suspend(member, { suspended, reason }),qualify(program, member, { qualified, reason })andresolveReward(entitlement, { reason })replace the deprecatedreferrals.control({ kind, ... }). Each generates itsactionIdwhen omitted.
Duplicate and misleading names have clear replacements; the old names stay as deprecated aliases:
management.points.record(input)awards, spends, adjusts, reverses or sets a balance, replacingpoints.update.management.pointLedgers.list()andpointLedgers.create(input)replacepoints.ledgers()andpoints.createLedger(input).
The
{ id, ... }input schemas (publishDeploymentSchema,submitDeploymentSchema,rejectDeploymentSchema,promoteDeploymentSchema,rollbackDeploymentSchemaandreferralControlSchema) are deprecated beside them;deploymentRejectionSchema,deploymentActionSchema,referralSuspensionSchema,referralQualificationSchemaandreferralRewardResolutionSchemadescribe the new bodies.The routes that took these IDs in the body keep answering released clients. Deprecated forms are removed in 2.0.0.
-
b3d87d0:
points.awardedwebhook events now carrysource,{ kind, id }, naming what paid the award:questorleaderboardwith the quest or board ID,engagementwith the engagement rule's key, ormanualwithid: nullfor an operator's point award.kindis an open string that can gain values;PayoutSourceKindfrom@domino-sdk/relay/servernames the known ones. A completion revocation result (CompletionRevocation, frommanagement.completions.revoke) gains the same optionalsourceas completion records, so a revoked leaderboard prize can be told apart from a quest completion;questkeeps carrying the board ID for a prize. See the webhook reference. -
5ddcb62: Approving a leaderboard settlement now sends a
completion.createdwebhook event for each winner's completion. Before, only quest attempts sent it. Everycompletion.createdevent now carriessource({ kind, id }, wherekindisquestorleaderboard) andkey, the occurrence the completion consumed. A prize event hasattempt: null, the board ID inquestandsource.id, and the settlement ID inkey. If your receiver treats everycompletion.createdas a quest completion, branch onsource.kind. Completion records in management snapshots gain the same optionalsource, andCompletionSourceKindis exported from@domino-sdk/relay/serverand@domino-sdk/relay/schema. See the webhook reference.
Patch Changes
-
75fa01c: Prerequisite-only automatic quests now try an uncompleted occurrence again after a claim-time budget rejection when a later member event passes current eligibility and budget checks. Evaluator, staff and revoked-occurrence rejections stay final. Automatic outcome reads can include optional
linkedAttemptdetails; the legacyattemptID remains available in 1.x but is deprecated. Operators can preview and confirm budget recovery through the existing automatic catch-up API and Console flow. -
8d049d4: The authoring reference now says that
ctx.wallet.balance()reads any Cosmos SDK chainmanagement.walletBalanceChains()lists, not only Cosmos Hub and Osmosis. Domino configures these chains and their endpoints; quest code and project settings can't supply an RPC or REST URL. For an unlisted chain or other on-chain state, send records from your own indexer to a custom tracker source. Code changes aren't needed. See token balances. -
6f10a51: Engagement rule payouts now earn referral percentage bonuses. Each daily engagement settlement counts as earnings of the rule's qualifying quest, so a bonus in the same ledger that covers that quest, or names no quests, pays the member's inviter in the same commit. Previously these payouts never earned a bonus. Bonuses apply from the next settlement; earlier engagement payouts are not backfilled. The agent referrals reference describes the rule. See percentage bonuses.
-
f322ced:
ctx.review.photo()is deprecated and will be removed in 2.0.0. It still works, and nothing about how it reviews photos changes. In new quest code, review an uploaded photo withctx.images.review({ images: [{ evidence: ctx.input.photo }], criteria })instead. It returns the samepass,failorunclearresult, and it can also review X images, reference images and up to four images at once. See check images.The agent references and capability map now list tracker-restricted ingestion keys (
ingestTrackersonmanagement.tokens.createandmanagement.tokens.scope) as supported. -
89f742b: Leaderboard prize points no longer earn referral percentage bonuses. Before, a prize award counted as a quest award, so a bonus that named no quests paid the winner's inviter on it, while a bonus with a
questslist never did. The points that won the prize already earned their bonuses. Bonuses already paid on earlier prizes stay, and revoking such a prize still corrects them. Quest, engagement and opted-in external awards are unchanged. The agent referrals and authoring references describe the rule. See percentage bonuses. -
f493c48: Referral corrections and operator reversals now consume one shared remainder for each positive payout, so either order settles the payout once. Historical corrections use the referral account's settled amount as a conservative cap. New referral definitions refuse Discord role rewards with a clear error. Stored legacy programs remain readable and recoverable, and skip an unsupported role with an Audit history reason instead of failing qualification. See referral corrections.
-
6e9b120: Completion revocation now keeps any points it could not take back visible after the command returns. The result and audit event report the remainder for each point ledger. Console shows the remainder on the member and attempt, and operators can open the existing point adjustment to settle it explicitly. Relay never records member debt for the remainder.
-
0ec4d7d:
RelayClientandManagementClientare now named interfaces thatcreateRelayClient,createBrowserRelayClient,createManagementClientandhostedManagementClientreturn, so declaration files name them instead of repeating the whole client.ManagementClient.testMembersis no longer optional: every client these factories create has it. The clients and the error classes (RequestError,RelayError,QuestUpdatedError,ManagementError) are not for implementation or subclassing; minor releases add methods and fields to them. A@compattag in a type's doc comment now says how it may grow in a minor release (Upgrade to Relay SDK v1). -
f6b705a: Revoking a completion now reads the member's recorded tier grants instead of every completion event in the project, so it stays fast as a project grows.
409 completion-grant-history-missingremains only for older completions whose original record could not be read; contact Domino support to restore their history. See Revoke a completion. -
Updated dependencies [4651887]
-
Updated dependencies [75fa01c]
-
Updated dependencies [547600e]
-
Updated dependencies [6c80795]
-
Updated dependencies [6088922]
-
Updated dependencies [6fa9cd5]
-
Updated dependencies [e3a050e]
-
Updated dependencies [b145e16]
-
Updated dependencies [3831c73]
-
Updated dependencies [b3d87d0]
-
Updated dependencies [f322ced]
-
Updated dependencies [5ddcb62]
-
Updated dependencies [f493c48]
-
Updated dependencies [6e9b120]
-
Updated dependencies [0ec4d7d]
-
Updated dependencies [f6b705a]
-
Updated dependencies [65a9765]
- @domino-sdk/relay@1.6.0
1.5.2
Patch Changes
- 68a30f4:
domino devkeeps the app running when the process pauses for longer than its 90-second preview lease, as when a laptop sleeps or a playground VM is paused. On resume it claims the preview again and reconnects the tunnel without restarting the dev server, so the preview URL and port stay the same. A dropped lease renewal or a tunnel connector that exits now retries with backoff instead of stopping the app.domino devstill stops when another run has taken over the checkout's preview, when Domino refuses the preview (for example after publish permission is revoked), or when the API stays unreachable for about two and a half minutes. - @domino-sdk/relay@1.5.2
1.5.1
Patch Changes
- 9fe0ec9: Point the moved-project message, the app-secrets capability, the bundled agent references and the starters' "no sign-in method" notice at Console's new Settings pages: API keys, App login keys, App secrets, Sign-in, Profiles and Staging access.
- @domino-sdk/relay@1.5.1
1.5.0
Minor Changes
-
625dcbc: Expose project creator IDs and resolved operator details in project access and audit responses. Validate enriched profile IDs against the recorded creator or actor while accepting older unenriched responses. Add
updateProjectfor Console organization admins to rename projects or change their slug with name confirmation. Hosted build links follow the current slug without a rebuild, including older builds that used repository-based addresses. Links switch only after the new hostname is confirmed.Slug changes preserve app secrets and hosted stores. New staging addresses are registered before the change commits; old and prepared slugs remain reserved until project purge, so interrupted changes can be retried safely. Reserved slugs cannot be claimed by older servers during a rolling upgrade.
Patch Changes
- Updated dependencies [625dcbc]
- @domino-sdk/relay@1.5.0
1.4.0
Patch Changes
- Updated dependencies [da46e7b]
- @domino-sdk/relay@1.4.0
1.3.0
Patch Changes
- Updated dependencies [86114a5]
- @domino-sdk/relay@1.3.0
1.2.0
Minor Changes
-
7f69653: Add test-environment KV and D1 storage for Domino-hosted Worker apps through
relay.jsonapp.storage. Stores receive native Worker bindings and retain data when declarations are removed. SQL migration files run before staging activation, with Wrangler-compatible tracking and per-file rollback and safe retries after interrupted responses.Inspect declarations and retained stores with
domino storage list. Connected development and staging warn when local Wrangler bindings differ from hosted declarations. See hosted storage and migration recovery. -
cdea4a0: Browse hosted app storage read-only and delete stores no serving build declares.
domino storage tables,rows,keysandget, andmanagement.storagein the SDK, read SQL tables and KV values withpublish;domino storage deleteremoves an undeclared store and its data. See browse and delete stores.
Patch Changes
- Updated dependencies [7f69653]
- Updated dependencies [cdea4a0]
- @domino-sdk/relay@1.2.0
1.1.1
Patch Changes
- 1d2a0cc: Recover saved submissions after the server permanently retires an operation. Preserve the terminal state across reloads, expose it for participant review, and allow explicit discard without silently submitting another claim. Starters show a separate restart action; unknown conflicts and lost responses still resume the original action. See the participant recovery reference.
- Updated dependencies [1d2a0cc]
- @domino-sdk/relay@1.1.1
1.1.0
Minor Changes
- ead35b5: Add Console-session-only management methods to create, list, act as, and reset test members in the test environment. Member reads expose the test marker and display name. Document the CLI exclusion and reset restrictions.
Patch Changes
- Updated dependencies [ead35b5]
- @domino-sdk/relay@1.1.0
1.0.0
Major Changes
-
ad91102: Record prerequisite-only automatic decisions, expose participant and operator outcomes, and add previewed catch-up for paused events with review permission and audited reasons. Point-entry changes schedule current eligibility asynchronously after commit, including refunds and referral or leaderboard awards. Operators can inspect and retry failed scheduling.
Remove the permanent silent pause block. Outcome pagination now merges prerequisite and tracker decisions; reset saved cursors and use the
automaticidentity on rows without a tracker record. See the v1 upgrade guide. -
4205914: Add code-authored campaigns with half-open availability windows and durable campaign-wide enrollment; typed completion, live balance and project-group prerequisites; typed availability blockers; and live quest-budget capacity on participant views. Participant and management clients can enroll a member in a required campaign, and Console shows campaign rules and member enrollment.
completed(quest)now returns a discriminated completion prerequisite instead of{ quest }, andprerequisiteSchemaaccepts only typed prerequisite definitions. Stored legacy releases remain readable. See Prerequisites are typed eligibility rules in the v1 upgrade guide. -
6d66f96: Add reversible campaign enrollment. Participants can withdraw and re-enroll themselves; trusted servers and Console operators can withdraw a member with an audited reason and restore them. Withdrawal blocks new admissions while preserving the first enrollment time, admitted attempts, completions, and rewards.
-
fa16503: The CLI's automation contract is frozen for v1. Every failure has a stable error
code, and the exit status says what kind of failure it was: 0 success, 1 failure, 2 usage error (was 1), 3 a change that may have taken effect without confirmation (was 1), and 130 cancelled by SIGINT or a prompt, 143 by SIGTERM (was 1). With--json, the error on stderr's last line always haserror,codeandoutcome, plusstatus,apiCode,detailsoroperationwhen they apply; Commander'scommander.*codes are gone. An uncertaindeploypublication orstagebuild reports anoperationwhoseresumearguments pin the full scope and finish it from any directory, announced as a JSONoperationevent on stderr right before the request is sent.deploy --resumeandstage --request-idnow require that scope as flags and need no checkout.doctor,checkandmigratealso write their failure to stderr. The undocumenteddomino GET /pathspelling is removed; usedomino api GET /path.domino helpwith an unknown command fails withUNKNOWN_COMMAND.domino devis now listed under Experimental commands. Result and error JSON Schemas, fixtures, a command inventory andcli/contract/index.mjs(the zod schemas by name) ship incli/contract/, anddomino reference automationexplains them. To upgrade, see CLI errors have stable codes and exit statuses and Call the management API withdomino api. -
a803b30: Evaluators now replay deterministically. Hosted evaluation freezes the clock for each attempt and seeds randomness from the attempt, so a retried or replayed attempt makes the same decision and
keyed()completion key. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.ctx.nowis new: the attempt's evaluation time in epoch milliseconds, the same on every run of the attempt.keyed()receives it too. It is distinct fromctx.eligibleAt, which is when the activity happened.Date.now(),new Date()andDate()returnctx.nowand do not advance, even across awaited capability calls.performance.now()returns 0.Math.random(),crypto.getRandomValues()andcrypto.randomUUID()repeat the same sequence for one attempt and differ between attempts.
Who should act. Evaluators that measure elapsed time or rely on unpredictable random values. Use
ctx.nowfor "as of evaluation" checks andctx.eligibleAtfor when the activity happened. See Evaluators see a frozen clock and seeded randomness in the v1 upgrade guide. -
e1cba88: Observations are removed, and every automatic activity arrives as a tracker record.
automatic({ source, type }),ctx.observation,management.observations.submit,POST /management/v1/observations, theObservationtype,observationSchema,observationInputSchema,observationReceiptSchema,observationTriggerSchema,discordReactionSchema,QuestView.trigger.observation(and the release trigger'sobservation) and theobservationfield of theautomaticavailability are gone.questStatusandrunsSilentlyfrom@domino-sdk/relay/reactnow label every tracker-triggered quest "Automatic", not only observation and keyed quests. A server that reported observations sends records to a custom tracker withmanagement.trackers.ingestand triggers quests withautomatic({ tracker }). Ingestion never joins a member to a project: a tracker trigger skips a member who has not joined withnot-joined. Quests published with an observation trigger leave the active set when Relay upgrades. Republish them from code on a tracker; the quest key can stay the same.Discord messages and reactions are two managed tracker sources,
discord.messages()(with{ text: true }to store message text) anddiscord.reactions(), on thediscordobject from@domino-sdk/relay/authoring. Domino's Discord gateway writes each new message or reaction into every tracker of that kind in the projects connected to the server, and keeps a record only when one of its accounts is a Discord account a member has linked. Their record data is typed (DiscordMessageData,DiscordMessageTextData,DiscordReactionData), sowherefilters andctx.tracker.record.dataare checked. A reaction record names the message's author as themessageAuthoraccount:automatic({ tracker: reactions, beneficiary: "messageAuthor" })replacesreaction.received, and Relay skips reactions to one's own message withbeneficiary-is-author. Discord trackers refuse ingestion (403) and refresh (422). Streaks work on these triggers.The
observepermission is removed. Ingesting needsingest, and enrolling a member from a server (management.quests.enroll) needs the newenrollpermission. Editors holdread,publish,inventory,points,ingestandenroll; hosted-app keys holdread,pointsandingestand cannot enroll. A personal API key issued withobservekeeps its other permissions, andobservegrants nothing: reissue it with Ingest or Enroll. Theobservation-questscapability is removed fromcapabilities.json; Discord tracker triggers are part of thetrackerscapability.To upgrade, see Observation triggers become tracker triggers and The
observepermission is replaced byenroll. -
23c85ba:
defineEngagementRewardtakesbudgets, a list of{ scope: "project" | "member", period: "day" | "week" | "month" | "lifetime", limit }, instead ofbudget: { projectDaily, memberDaily, projectLifetime }. Periods are those of the project calendar at settlement time, so a rule can now cap weekly or monthly payouts, and accounts settle once per project calendar day instead of once per UTC day. A definition withbudgetfails validation; rules already published keep their limits in the new form.See Engagement budgets are a list of periods in the v1 upgrade guide.
-
6cd0ba7: Make protocol 2 the first stable evaluator contract, including the required pinned project calendar. Remove pre-release protocol 1 from publication and execution, and preserve an SDK-built protocol-2 compatibility fixture. Rebuild and republish pre-v1 bundles during the approved cutover; changing only their protocol field is insufficient. See the evaluator protocol upgrade guide.
-
3a38259: Run content qualification and performance gates on custom content sources as well as X. Declare each custom source's body schema and metric names, use
content({ tracker }), and read the captured body throughctx.tracker.content. Content quests imply one completion per item. Replace retired content admission and approval calls with quest outcomes, claims and tracker outcome retries.Upgrade steps: Content stages.
-
4626004: Identity kinds are open. Besides
wallet,x,discordandmember, a custom tracker can declare your app's own kinds,app:<name>such asapp:telegram, and records name your app's user IDs. An identity integration created withidentityKind: "app:<name>"(set only at creation, kept by rotation) links each subject it signs in to the member as an identity of that kind, and tracker triggers attribute records through it, with{ provider: "app:<name>", subject, revision, linkedAt }asctx.tracker.attribution.account.A kind belongs to the integration that first claimed it, also after a disconnect, which keeps its links attributing. A new integration takes a kind that has identities only by adopting them:
adoptIdentitiesFrom(the old integration's ID, from the newmanagement.identityIntegrations.kinds()) with areasonthe audit log keeps; otherwise it is refused with 409. Reviewers unlink and relink a member's app identity (management.members.unlinkIdentityandrelinkIdentity, and Console's Tracker identities), each audited with a reason: unlinking stops attribution, ends the member's sessions for that subject, and refuses its sign-in with 409APP_IDENTITY_UNLINKED, whichcreateParticipantBackendpasses on, until it is relinked. A subject the integration already signs in as another member answers 409APP_IDENTITY_CONFLICT.Evaluators read the member's selected X and Discord accounts and app identities with
ctx.member.identities(), pinned once per attempt andnullwhen there are none. Management member reads return them asidentities, and Console lists them on the member under Tracker identities.Published tracker definitions in status, list, record-page, summary, and deployment responses use
trackerDefinitionResponseSchemaandTrackerDefinitionResponse. Authoring keeps the stricttrackerDefinitionSchemaandTrackerDefinition.Responses and evaluator contexts read identity kinds as an open set,
OpenIdentityKind, and ignore fields they do not know, so a kind a later Relay adds no longer fails them.Breaking:
IdentityKindis no longer a closed union,identityKindSchemais no longer az.enum, response kinds areOpenIdentityKind,trackerRecordSchemais a separate response schema,trackerAttributionSchema's account is a plain union with app and generic variants, andctx.memberis{ id, identities() }. See Identity kinds are open, and evaluators readctx.member.identities(). -
2149973: Read published trackers with future source kinds without rejecting a tracker list. Preserve unknown source payloads and expose
isKnownTrackerSourcefor supported-source narrowing. Tracker authoring remains closed. Update code that assumes every response source is supported as described in the v1 upgrade guide. -
54fcf41: The participant client follows one convention for paging, sessions and errors.
- Every method takes an optional last argument,
{ session?, signal? }.sessionreplaces the positional session argument ofwallets.challenge,wallets.verifyandme.collectionPass. Calls Relay binds to a session (wallet linking, collection passes,quests.retry) read the current session whensessionis omitted. - Every paged read takes
{ cursor? }and returns{ items, nextCursor }:referrals.history,quests.outcomes,content.qualifications,content.performance,engagement.pageandengagement.boundarieschanged frombefore,afterandnext. The participant routes answernextCursorand readcursor, and so do the management routes and client methods that share them (management.referrals.history(member, { cursor }),management.content.*,management.engagement.pageandboundaries). - Every rejection is a
RelayError, which extends the new sharedRequestError:kind(http,network,timeout,aborted,invalid-response,client),uncertain(true only for a write that may have taken effect: a failure after sending, a cut-off body, a 500, 502 or 504, or an unreadable success answer),statuswhenever response headers arrived, an open stringcode(known ones inRelayErrorCode),operationId, andrecovery(replaywith anoperationId, elsereconcile) when uncertain.isRelayErrorandisRequestErrorrecognize these errors across duplicate copies of the package.RelayError.statusis nownumber | undefined. Invalid input rejects withinvalid-inputinstead of aZodError, a success answer this SDK cannot read withinvalid-response, a body cut off after its headers with kindnetworkand its status, and a wallet signer's own failure withwallet-signer. The error body is read field by field, so Relay's message and a code the SDK does not know are always kept. Relay now sendscode: "not-signed-in"andcode: "stale-session"on those participant errors, and every paged read answers a malformed cursor with 400, codeinvalid-cursor, and one message. - Removed:
submit,retry(id),progress(member),upload(member, file),auth.connections,me.linkPass, thewallet.*namespace, and the staff calls the participant API refused (lookupMember,snapshot,publish,publishBatch,beginHandover,confirmHandover,decide,journeyTemplate,template). - Parameters take Zod input types, so defaulted fields are optional, and cursors are opaque strings the server validates. The session the client returns (
ParticipantSession) accepts fields and principal kinds a newer Relay adds;sessionMember(session)reads its member. - Hosted proxies no longer forward the 0.x single-wallet routes (
/v1/me/wallet,/challenge,/verify).
Upgrade: One paging, session and error convention in the participant client in the SDK v1 upgrade guide maps every method and has search commands.
- Every method takes an optional last argument,
-
c832384: Replace raw authoring resource keys with typed quest, ledger, tracker, and group handles. Authoring constructors and nested budget, tier, milestone, and bonus definitions now use
key; ledger declarations publish from modules; point-ledger settings return typed references. Stored releases and management, Console, and participant wire IDs remain compatible. See Authoring references use typed handles in the v1 upgrade guide. -
23c85ba: Each project environment has one project calendar: a timezone, UTC until you choose one, that sets where every day, week and month begins. Streak days, quest budgets, engagement budgets and calendar leaderboard windows all follow it, so a weekly budget and a weekly leaderboard cover the same week.
- Set it with
"calendar": { "timeZone": "Europe/Budapest" }in relay.json, in Console Settings → General → Project calendar, or with the newmanagement.calendar.read()andmanagement.calendar.configure({ timeZone, reason })(GETandPOST /calendar,publishpermission; in live only from Console). A change takes effect at the next midnight in the current timezone: the open day, week or month finishes in the old zone, and streaks stay consecutive. A deployment applies relay.json's timezone only when it differs from the last one deployed, and the deployment preview's newcalendarfield shows the change it schedules. streak()and calendar leaderboard windows no longer take atimeZone; both throw if given one. Streaks and boards already published with one still read.- Budgets reset at midnight in the project timezone instead of UTC, and accept
period: "month". - One validator,
timeZoneSchema, accepts named IANA zones only. Fixed offsets such as+02:00andEtc/GMT-2are rejected, including in leaderboards, which used to accept them. The sharedcalendarPeriodSchemaislifetime | day | week | month.
Who should act. Projects that pass
timeZonetostreak()or a calendar leaderboard window, and projects that rely on budgets resetting at UTC midnight. See Set the timezone once, on the project calendar in the v1 upgrade guide. - Set it with
-
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
-
89ea6ba: Add reviewed live project publication. The management client can list deployment records, submit and reject previews, promote a published test deployment, and prepare a rollback to the previous live deployment. Promotion and rollback accept an action UUID, and the management client generates one when omitted so retries return the same deployment record.
domino deploy --environment livenow returnsstatus: "pending", and--promotesubmits test source for live review; only a person in Console can approve, reject, or prepare rollback. The newpendingdeploy status is a breaking addition for exhaustive automation. See Live deploys return a pending review. -
5d4ec3e: Release Relay SDK and CLI 1.0.0. Install the SDK and CLI together at the same version. This release removes pre-v1 APIs; follow the SDK v1 upgrade guide before replacing a 0.x installation.
Stable APIs follow semantic versioning throughout the supported v1 lifetime. Compatibility exemptions apply only to explicitly experimental entrypoints.
-
ae047ca: Remove the pre-v1 runtime, release, single-wallet and tracker compatibility shims. Published quests must carry an explicit evaluator protocol; protocol 2 is the first stable contract, and all pre-v1 bundles must be rebuilt and republished. Participant and management reads now expose only canonical wallet lists, and tracker policies use canonical identity roles and an explicit observation mode. See Wallet reads return every linked wallet in the v1 upgrade guide.
-
bdb89a4: Evaluator readers follow one rule: a missing subject is
null, a failure throwsCapabilityError, linked accounts are pinned once per evaluation run, and tracker reads are point in time. Retries replay the same run; scheduled deferred evaluations get fresh captures. Pre-v1 releases must be rebuilt and republished during cutover.- Missing is
null.ctx.x.profile(),follows()andreposted()returnnullwithout a linked X account instead of throwing;ctx.x.post()returnsnullfor a post that does not exist and needs no linked account.ctx.discord.member()returnsnullwithout a linked Discord account.ctx.wallet.list()returnsnullinstead of[], andctx.wallet.balance()returnsnullinstead oftotal: "0"when the member has no wallet of that family.ctx.trackers.content()returnsnullfor a missing record or observation. See Readers returnnullwhen an account is not linked or a subject is missing. - Failures throw
CapabilityError, new in@domino-sdk/relay/authoring(its wire shape,capabilityFailureSchema, is in@domino-sdk/relay/schema), withcode("not-linked","provider-unavailable","author-error") andretryable. Invalid capability input isauthor-error; an image check of{ x: "profile" }without a linked X account isnot-linked. Failures that are not retryable are captured and replay. A retryable failure that quest code handles and moves past is final for that evaluation run; one that escapes the evaluation is released, along with every fact captured after it, and retried, so the retry asks the provider and the later calls again. Rethrow a caught failure, or wrap it with{ cause: error }, to let it be retried. The failure shape is open: a newer Relay can add codes, and the SDK treats an unknown code as not retryable. See Capability failures throwCapabilityError. - Tracker reads are point in time.
ctx.trackers.records()andsummary()takeasOf, defaulting toctx.eligibleAt, and every tracker read of an evaluation run sees the observations stored at its first tracker read. PassasOf: ctx.nowto count everything stored by then. See Tracker reads are point in time and pinned per attempt. - Linked accounts stay pinned. X and Discord readers pin the account on the evaluation run's first read and replay that pin on retries, as wallets already did, so a link change no longer fails the attempt with "X account changed" or "Account or integration changed". See Linked X and Discord accounts stay pinned for the attempt.
Who should act. Evaluators that read
ctx.x,ctx.discord,ctx.walletorctx.trackers, or that match capability error messages. The changed return types point TypeScript at each read to update. Thediscord-membership,token-holderandcommunity-screenshotsnippets and the Discord starter'sjoin-the-serverquest are updated. - Missing is
Minor Changes
-
afe1128: Operators can revoke an accepted completion with a reason:
management.completions.revoke({ completion, reason, freeOccurrence })(POST /management/v1/completions/<id>/revoke,reviewpermission). In one transaction the completion stops counting (progress, prerequisites of new attempts, counts, profiles and referral qualification), its point awards are reversed as far as the member's available points allow, tiered rewards it granted are voided or lowered to the tier another completion grants (collected ones are flagged), Discord role grants not yet delivered are stopped while delivered roles are flagged for the operator to remove in Discord (Relay never removes roles; automatic removal is DOM-99), referral programs that qualified on it are re-evaluated, and a streak milestone it paid is revoked while its days stay.freeOccurrencelets the member complete the quest again for the same completion key; by default later attempts for it are rejected. The result reports each part of the cascade. API keys withreviewpermission can revoke in live. Console offers it on member detail and accepted attempts, and the attempt history and Audit history show the revoke and its reason.domino capabilities --jsonnow markscompletion-revokeas native.Breaking: a reward delivery's
actionis an open string (RewardDeliveryActionlists"grant-role"), the worker fails a delivery whose action it does not know, and a grant still pending at the revoke fails. The revoke result'soutcomefields are open strings with known values inRevokedEntitlementOutcome,RevokedDeliveryOutcomeandRevokedReferralOutcome. To upgrade, see Reward delivery actions are open strings.Revoking preserves percentage bonuses from other valid completions and queues a new automatic attempt when a freed occurrence still meets its prerequisites. Delivery responses add optional
dispatchedAtandrevokedAtfields. Dispatch history survives retries, revoke fences failed generations, and late provider success stays visible without reopening delivery. Console uses the revoke field to stop retries and shows unknown delivered actions by their generic status.Revocation refuses missing or unreadable grant history before changing member state. Collected entitlements carry a persistent recovery flag in ordinary reads and Console. Correcting collection clears the flag and voids an unearned reward instead of reopening it; a surviving grant reopens only its earned tier. Reused-completion attempt timelines include the shared completion’s revoke event.
-
b0bffa0: Add native distinct-action progress counters with calendar and rolling windows, durable achievements, participant summaries and audited operator correction. Participant state follows settlement order for late-arriving repeat batches, and achievement alerts remain scoped to their calendar window when the next window reopens. Add qualified-referral and continuous-membership examples. See counter rules. Update custom attempt renderers using the counter upgrade steps.
-
e7f81ff: Add signed outbound webhooks for operator backends, with scoped subscriptions, bounded retries, delivery logs, replay, and overlapping secret rotation. Expose
management.webhooks,verifyWebhook, shared activity constants, anddomino webhookscommands. This is additive; existing integrations need no changes. See Receive outbound webhooks for an optional polling migration. -
903ffad: Persist recoverable CLI mutations before sending and add
operations list,showandresume. Creation resumes through repository and checkout failures; quest lifecycle changes retain their action ID and scoped input. Errors for known partial failures may now include an operation to resume. See the saved CLI mutation recovery upgrade guide. -
8465d40: Operators can correct a member's tiered reward, at the revision they saw and with an audited reason.
management.handover.cancel(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/cancel,handoverpermission) returns a handover in progress toavailableand puts its pickup size back into stock.management.entitlements.correctCollection(…/correct,review) does the same for a collection recorded by mistake, andmanagement.entitlements.void(…/void,review) voids an available entitlement. A stale revision fails with 409entitlement-changed, the wrong starting status with 409entitlement-status. API keys with the permission can make these corrections in live. Console Rewards and member detail offer them, and Audit history lists each one with its reason.domino capabilities --jsonlists them asentitlement-corrections.Breaking:
Entitlementgains the status"void", withvoidedAt. A voided entitlement cannot be collected; a quest that grants the same reward to the member again makes itavailable, as a fresh grant at that quest's tier; a voided referral reward stays void. SDKs from before this release fail the whole progress read once a member has a voided entitlement. Reads now parse entitlements with a tolerantentitlementSchema(fields a newer server adds are kept, and an unknown status reads as"unsupported"withreportedStatus); the strict stored shape isentitlementStateSchema, and the management client'sentitlementRecordSchemais removed in favor ofentitlementSchema. To upgrade, see Entitlements can bevoid, and reads tolerate statuses they do not know. -
d1ef769: Relay answers 503 only when it provably did nothing, so a write it reports as failed with 503 is safe to send again under a new action ID:
- An unexpected server error is 500, not 503. It can follow a commit, and the SDK, the management client and the CLI already treat 500 on a write as
uncertain. - A lost or unusable answer from an internal service is 502, or 504 when it timed out, where it was 503: publication, the Discord integration service, the preview service, Clerk API key changes, and the cloud builder ("Could not start cloud build. Retry with the same request ID." is now 502 or 504).
- 503 stays for refusals before any work: missing configuration, a dependency check that fails before the transaction, and a data space closed for maintenance, which now sends
Retry-After: 60. - Rate-limited answers (429) send
Retry-Afterwith the seconds until the limit's window resets: sign-in starts, email codes, wallet sign-in and wallet linking, CLI device login and approval, identity exchange, profile and avatar changes, playground starts, preview starts, development keys, tracker previews and feedback.
The
participant-apiandhostedagent references describe the 503 rule and the 502 or 504 build start.Upgrade custom retry handling using Treat failed writes as uncertain when Relay cannot prove refusal. Evidence and avatar PUTs, email delivery, Clerk cleanup, and activation failures after a commit now preserve uncertainty. Staging password limits also carry
Retry-After. - An unexpected server error is 500, not 503. It can follow a commit, and the SDK, the management client and the CLI already treat 500 on a write as
-
71cf912: Add reviewable leaderboard settlements. Fixed and calendar earned/net leaderboards can declare point and tiered-reward prizes, choose shared-rank or earliest-score tie handling, and freeze each ended window with its eligibility and exclusions at cutoff. Entries received after the cutoff stay out even when backdated, and a definition with a changed window advances past an obsolete scheduled cutoff. Operators review and approve delivery in Console or with
management.leaderboards, where the approval reason remains visible, and can correct an individual winner through completion revocation without awarding that settlement again. -
f2f1b75: Breaking: the management client moves from
@domino-sdk/relay-clito the new@domino-sdk/relay/serverentrypoint, withcreateManagementClient,hostedManagementClient,ManagementError, the management schemas and types, andstarterTemplates. Server code, including a hosted app's Worker, now needs only@domino-sdk/relay, without the CLI's command-line parser and TypeScript compiler.@domino-sdk/relay-cliis only thedominoexecutable and its agent skills; it has nothing to import.serverBoundary()also fails a browser build that imports@domino-sdk/relay/server. To upgrade, see Import the management client from@domino-sdk/relay/server.The capability catalog (
domino capabilities --json) adds staff confirmation, staff handover, stock, point corrections, deploy and stage, operator member reads and lookup, project access, and the management API withdomino api. It marks webhooks, bulk data export, completion revocation and tracker-scoped keys as unsupported, and lists the management methods it leaves out on purpose inexcludedManagementMethods. -
8c06912: Add operator reads and audited unlink for current X and Discord quest accounts. Reviewers provide the subject and selection revision to prevent a stale review from unlinking a newer connection. Identity ownership, sign-in grants and existing rewards remain unchanged. Console exposes the action on each member's Quest accounts section.
-
c320586: A content quest's
performance.evaluatecan no longer select rewards. It gates the rewards the content evaluation already selected, and it always did: arewardsvalue in its result was silently ignored. Its type is nowPerformanceOutcome(accept,revieworrejectwith a reason only), sodecision.accept({ rewards })anddecision.review({ reason, rewards })are type errors there, and a runtime result that carriesrewardsfails the evaluation with "A performance evaluator cannot select rewards". To change what a post earns, select rewards in the quest'sevaluate.See Performance evaluators no longer select rewards in the upgrade guide.
-
c320586: Replace untyped prerequisite objects with typed rules.
completed(questHandle)creates a completion rule withscope: "ever";completed(questHandle, { scope: "period", period: "day" })checks the current project-calendar period. Balance and group requirements usebalanceAtLeast(ledgerHandle, minimum)andinGroup(groupHandle). Persisted rules carry an explicitkind; bare{ quest }and string prerequisites are not canonical v1 input.See Prerequisites are typed eligibility rules and Authoring references use typed handles in the upgrade guide.
-
569989e: Reconcile Discord role membership across projects and environments, preserve external assignments, and expose operator recovery plus current membership alongside delivery history. Revoking the last accepted grant now removes Relay-owned assignments. Read the SDK v1 upgrade guide before upgrading.
Treat forbidden Discord membership reads as permanent delivery failures so operators can fix permissions and retry immediately.
Publish initial pending delivery events in the grant transaction so observers can see queued rewards before reconciliation starts.
-
8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
-
11ab1d8: Add explicit archive and restore for code-declared resources through the management client, durable CLI operations and Console. Keep resource identities and history while stopping new activity, and preserve archive state across publication. See the resource lifecycle.
-
ddfc526: The participant client, the management client and the CLI share one set of deadline, cancellation and retry rules.
- Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
createRelayClient({ timeout })and the new per-call{ timeout }option of every participant method set it, astimeoutalready does for the management client. A timeout is never retried. - A call is sent again, at most three times in all, only when the server applies a repeat at most once: reads; participant writes with an action ID, submission evidence by its upload ID,
wallets.verify(by its challenge) andwallets.challenge, but not avatar uploads, whose upload ID has no receipt; typed management writes whose input has anactionId; and the CLI'sdeploypublication,stagebuild request andquests archive/unarchive. Every attempt sends the same payload and session. Retries follow a lost answer, including a body that breaks off after successful headers, and HTTP 500, 502, 503 and 504, with a short jittered backoff. Invalid or unexpected answers, cancellations, rejected input, other refusals and every other write are sent once. - A 429 is retried only when its
Retry-Afterfits in the time left, and every wait honoursRetry-After. The newretryAfterfield ofRequestError(and soRelayErrorandManagementError) carries the wait the server asked for, in milliseconds; the CLI reports it asdetails.retryAfteronRATE_LIMITEDandSERVER_ERROR. - A session-bound participant call that reads the current session first shares one deadline with that read, and
auth.session()honourstimeout. - An error status whose body breaks off, times out or is cancelled is now a
network,timeoutorabortedfailure that keeps itsstatusandretryAfter, in all three clients; an error body that is not JSON stayshttp. - Cancelling with
signalstops a retry wait at once. A write any attempt of which may have been applied staysuncertain, with itsoperationIdandrecovery, whatever later attempts answer. management.requestretries only GET, andmanagement.rawsends once.useInvitation()keeps a rate-limited code for a retry instead of discarding it as refused.
Avatar failures report
recovery: "reconcile"even when they have an upload ID. Wallet address and signature waits reject when the caller cancels. See Bounded requests and avatar recovery for the recovery-handler change.Generic management writes report
recovery: "reconcile"even with a caller-supplied action ID. Uncertaindomino apiwrites exposedetails.recovery: "reconcile". See Generic management writes require reconciliation for the recovery-handler change. - Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
-
67d0d8c: Add bounded
decision.deferand absolutedecision.progressresults, fresh evaluation runs scheduled by durable timers, participant progress and deferred status, and consistent completion-key authoring types. Reviewers can reject a deferred attempt to stop it.Attempt response statuses accept future server values; use known
AttemptStatusconstants with a fallback. New manual submissions while deferred return409 attempt-pending. Custom decision/status switches must handle the new variants. See Handle deferred evaluator decisions and progress for old and new code, capture semantics, and search patterns. -
d3b6261: Restrict personal ingestion keys to selected custom trackers, inspect and edit their scope without changing the secret, and return TRACKER_SCOPE_DENIED for disallowed writes. Human CLI sign-ins can manage their own keys; project-scoped playground sessions and API keys cannot. Existing unrestricted keys retain project-wide ingestion.
-
d7f9fe4:
streak(...)completes manual and tracker-triggered quests. Each accepted attempt counts the project-calendar day of its trusted occurrence time, exposed asctx.eligibleAt: a tracker observation'soccurredAt, or the admission time of a manual submission. A rejected manual submission preserves committed streak progress and displays its reason. Once the streak milestone pays, later tracker observations do not pay it again.Content-stage quests and prerequisite-only
automatic()quests cannot use streak completion. A published quest cannot change between manual and tracker triggers under the same key. Observation triggers are removed; migrate them to trackers. See Observation triggers become tracker triggers. -
0a89168: Operators can archive point ledgers and correct points from Console member detail.
management.pointLedgers.list()includes nullablearchivedAt, andmanagement.pointLedgers.archive(id)archives a ledger. Archived ledgers remain in balance and history reads and existing published rewards keep working, but new reward pickers and published definitions reject them. -
0052894: The management client has typed methods for the routes Console and scripts called by path:
attempts.confirm,attempts.reviewandattempts.retry,passes.verify,handover.beginandhandover.confirm,operations.configure,inventory.addStock,quests.updateSettings,tokens,grants,teamandidentityIntegrations. The newmembers.lookup({ provider, subject })(GET /management/v1/members/lookup,readpermission) finds the one member of the project holding exactly that account, and fails with 404member-not-foundor 409account-ambiguous.Breaking: every failed call rejects with a
ManagementErrorwhosekind(http,network,timeout,aborted,invalid-response,client) separates a server refusal from a lost or unreadable answer and from input rejected before sending. It keeps the server'smessageand anycode, including codes this version does not know, and reportsuncertain, withoperationId(theactionIdit sent) andrecovery, when a write may have been applied. It extends the SDK's sharedRequestError;isManagementError()recognizes it.CreationErroris removed. Calls end after 30 seconds by default; every method takes{ signal, timeout }as its last argument, and the client takes a defaulttimeout. To upgrade, see CatchManagementErrorinstead ofCreationErroror a status property.Breaking:
member,people,unlinkMemberWalletandsetMemberWalletSignInmove tomembers.get,members.people,members.unlinkWalletandmembers.setWalletSignIn. See Member methods moved undermanagement.members. -
cf5b02c: Custom tracker records have a declared data schema.
customSource(namespace, { data })requires a zod object schema (zis exported by@domino-sdk/relay/authoring), stores its JSON Schema form with the tracker, and throws, naming the field, for what ingestion cannot check (string formats and patterns, tuples, refinements, transforms, dates, bigints). Publication refuses a custom tracker without one. Ingestion rejects a batch whose record data does not match with 422 and{ error, code: "invalid-record-data", record, path, reason }, one too costly to validate withrecord-data-too-complex, and records for a tracker published without a schema withtracker-schema-required. Schemas are bounded (32 KB, 16 levels, 256 properties, required keys, enum values or key names per node, 16 branches per union, no wide union nested in another union's branch), and validation charges every unit of work to a per-record and per-batch step budget. A deployment whose new schema breaks a live quest'swherefilter, Console quests included, is refused. Stored records are not rechecked: tracker triggers skip non-matching records with the new skip codeinvalid-record-data, evaluators reject pinned records that do not match the schema the quest was built with, andtrackers.records(handle)leaves them out and counts them inexcluded. The schema can change under the same tracker key and applies to later records.The data type flows into quests:
ctx.trackeris non-null in a tracker-triggered quest's evaluator withrecord.dataof the tracker's type (content triggers expose their captured body throughctx.tracker.content),trackers.records(tracker)returns typed records, andwherepaths,equalsvalues and{ setting }bounds are type-checked against the data and the quest's integer settings.wherealso accepts a list of conditions that must all hold, andautomatic()and publication check filter paths against the stored schema.settings.objectanddefineQuestaccept onlysettings.*builders; a plain zod setting is a type error anddefineQuestthrowsSetting "<key>" is not a quest setting.See Custom sources declare their record data and Quest settings come only from settings builders in the upgrade guide.
Patch Changes
-
1019218: Console Audit history now records every point ledger created in Console or with
management.points.createLedger, as Point ledger created, with the ledger's key and name, the environment and who created it. Before, only edits were audited. -
ed4988c: Read Cosmos Hub and Osmosis bank balances with
ctx.wallet.balance(), pinned to one verified block and the member's wallets on the exact Cosmos network. -
c320586:
defineQuestthrows at build time for two shapes that only failed when publishing: a bareautomatic()quest with norequiresentry, and a bareautomatic()quest withkeyed()completion. A bareautomatic()runs once its prerequisites are complete, so it needs at least one typed completion, balance or group prerequisite and completes once. The backend still checks both when it publishes. -
83c4426: Update Discord message tracker records when a message is edited or deleted. Turning text off or deleting a message now erases retained message text.
-
bd960a4: Only a person signed in to Console can create or edit a point ledger in live.
management.points.createLedgerandmanagement.pointLedgers.savewith an API key or a CLI sign-in now get 403 "Approve live publication in the console" in live, as publication already did; in test they work as before. To upgrade, see test and live. Live operator controls stay open to a personal API key withpublish: operation pauses (POST /management/v1/operations),management.referrals.control,management.leaderboards.excludeandgroup, andmanagement.trackers.control. Console Audit history now lists leaderboard exclusion, group and tracker control changes with the key's owner as the actor. The agent references andcapabilities.jsondescribe both rules. -
c320586:
manual({ actor: "staff", input })throws instead of dropping the actor. Its types never allowed the combination, but untyped callers got a quest that members could submit evidence to, not one staff confirm. Staff-captured evidence is not supported: usemanual({ actor: "staff" })for a staff confirmation, ormanual({ input })for evidence the participant submits. -
2f78766: Fix
domino checkanddomino migratewhen a valid manifest omits either discovery array. Omitted arrays use the same empty defaults asdomino build, and explicit resources remain included. -
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
710e520: Participants read their own point balances and history.
relay.me.balances()(GET /v1/me/points) returns{ items }: the signed-in member'sbalanceandavailablepoints (after reservations) in every ledger of the project, each with itsledgerreference.relay.me.pointHistory({ ledger, cursor? })(GET /v1/me/points/:ledger/history) pages through the member's own entries in one ledger, by key or ID, most recently recorded first, as{ items, nextCursor }. Each entry is{ id, amount, kind, at, quest, reverses }, without the operator's reason or actor.kindis an open string; thePointEntryKindconstants (award,spend,adjust,reverse) name the kinds this SDK knows, and referral payouts areadjust. An unknown ledger answers 404 with codepoint-ledger-not-foundand a member who has not joined the project 404 withnot-joined, both added toRelayErrorCode. Neither read takes a member, and both are scoped to the session's project and environment. React apps useuseBalances(), which reads again after a quest completes, andusePointHistory(ledger). The participant proxy and identity integrations admit both routes. New schemas and types:pointBalanceSchema,pointBalancesSchema,pointEntrySchema,pointHistoryQuerySchema,pointHistoryPageSchema,PointBalance,PointBalances,PointEntry,PointEntryKind,KnownPointEntryKind,PointHistoryQueryandPointHistoryPage.The classic starter's header now reads the balance with
useBalances()instead of its/api/me/pointsserver route, which is gone. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
-
1084288:
@domino-sdk/relaynow exports only the participant runtime client:createRelayClient,RelayError,QuestUpdatedError,formatUnits,parseUnits, and the types the client returns. Schemas, builders and avatar helpers moved to their own entry points, a few implementation helpers are no longer public, and two are no longer exported at all, so an app that imports the client no longer loads authoring builders, deployment and management schemas, or DiceBear.- Zod schemas (and helpers such as
identifier,caip10andxPostId) come from@domino-sdk/relay/schema. - Builders (
defineTracker,defineLeaderboard,defineReferral,defineEngagementReward,content,streak,settings,discord,pointLedgers,x,customSource) and evaluator reader types (TrackerReader,XReader,ImageReader) come only from@domino-sdk/relay/authoring. avatarSrc,defaultAvatar,defaultAvatarSvgandwalletAvatarSeedcome from@domino-sdk/relay/avatar, and no longer from@domino-sdk/relayor@domino-sdk/relay/react.- No longer public:
identityIntegrationSchema,identityIntegrationInputSchema,identityIntegrationSecretSchema,participantExchangeSchema,questConfiguration,parseImageResult,parseImageExtraction,isContentParticipantRoute,isProfileParticipantRoute,isWalletParticipantRouteandisWalletSignInRouteare exported only from@domino-sdk/relay/internal, which Relay's own services share and which is not a supported API. - No longer exported:
questActionsandreconcileQuestDraft. Open a quest controller withrelay.quests.open(view); itsquest-updatedresult already carries the reconcileddraft. - The package declares
sideEffects: false. esbuildis an optional peer dependency, needed only by@domino-sdk/relay/build.@domino-sdk/relay-clidepends on it directly, sodomino buildanddomino deploywork as before. Code that callsbuildQuestwithout the CLI must installesbuild.
Upgrade: Runtime entry exports only the client in the SDK v1 upgrade guide has the import mapping and search commands.
- Zod schemas (and helpers such as
-
e635fe2: Troubleshooting reference: when a build or typecheck fails on an API that no longer exists after an upgrade, read the Upgrade to Relay SDK v1 guide.
-
Updated dependencies [ad91102]
-
Updated dependencies [4b10080]
-
Updated dependencies [4205914]
-
Updated dependencies [6d66f96]
-
Updated dependencies [afe1128]
-
Updated dependencies [ed4988c]
-
Updated dependencies [b0bffa0]
-
Updated dependencies [bac9ca4]
-
Updated dependencies [c320586]
-
Updated dependencies [a803b30]
-
Updated dependencies [e1cba88]
-
Updated dependencies [1141af4]
-
Updated dependencies [e7f81ff]
-
Updated dependencies [23c85ba]
-
Updated dependencies [8465d40]
-
Updated dependencies [84feaf7]
-
Updated dependencies [6cd0ba7]
-
Updated dependencies [3a38259]
-
Updated dependencies [83c4426]
-
Updated dependencies [71cf912]
-
Updated dependencies [f2f1b75]
-
Updated dependencies [c320586]
-
Updated dependencies [4626004]
-
Updated dependencies [2149973]
-
Updated dependencies [8c06912]
-
Updated dependencies [6c30583]
-
Updated dependencies [54fcf41]
-
Updated dependencies [710e520]
-
Updated dependencies [6c30583]
-
Updated dependencies [c320586]
-
Updated dependencies [c320586]
-
Updated dependencies [c832384]
-
Updated dependencies [23c85ba]
-
Updated dependencies [2528cec]
-
Updated dependencies [569989e]
-
Updated dependencies [8a8870e]
-
Updated dependencies [11ab1d8]
-
Updated dependencies [7e37049]
-
Updated dependencies [c919948]
-
Updated dependencies [c2390b3]
-
Updated dependencies [909f343]
-
Updated dependencies [ddfc526]
-
Updated dependencies [8b706af]
-
Updated dependencies [89ea6ba]
-
Updated dependencies [67d0d8c]
-
Updated dependencies [d3b6261]
-
Updated dependencies [1084288]
-
Updated dependencies [5d4ec3e]
-
Updated dependencies [d7f9fe4]
-
Updated dependencies [ae047ca]
-
Updated dependencies [0a89168]
-
Updated dependencies [e1e701b]
-
Updated dependencies [cf5b02c]
-
Updated dependencies [1e23d8f]
-
Updated dependencies [bdb89a4]
- @domino-sdk/relay@1.0.0
1.0.0-next.34
Major Changes
-
ad91102: Record prerequisite-only automatic decisions, expose participant and operator outcomes, and add previewed catch-up for paused events with review permission and audited reasons. Point-entry changes schedule current eligibility asynchronously after commit, including refunds and referral or leaderboard awards. Operators can inspect and retry failed scheduling.
Remove the permanent silent pause block. Outcome pagination now merges prerequisite and tracker decisions; reset saved cursors and use the
automaticidentity on rows without a tracker record. See the v1 upgrade guide. -
4205914: Add code-authored campaigns with half-open availability windows and durable campaign-wide enrollment; typed completion, live balance and project-group prerequisites; typed availability blockers; and live quest-budget capacity on participant views. Participant and management clients can enroll a member in a required campaign, and Console shows campaign rules and member enrollment.
completed(quest)now returns a discriminated completion prerequisite instead of{ quest }, andprerequisiteSchemaaccepts only typed prerequisite definitions. Stored legacy releases remain readable. See Prerequisites are typed eligibility rules in the v1 upgrade guide. -
6d66f96: Add reversible campaign enrollment. Participants can withdraw and re-enroll themselves; trusted servers and Console operators can withdraw a member with an audited reason and restore them. Withdrawal blocks new admissions while preserving the first enrollment time, admitted attempts, completions, and rewards.
-
6cd0ba7: Make protocol 2 the first stable evaluator contract, including the required pinned project calendar. Remove pre-release protocol 1 from publication and execution, and preserve an SDK-built protocol-2 compatibility fixture. Rebuild and republish pre-v1 bundles during the approved cutover; changing only their protocol field is insufficient. See the evaluator protocol upgrade guide.
-
3a38259: Run content qualification and performance gates on custom content sources as well as X. Declare each custom source's body schema and metric names, use
content({ tracker }), and read the captured body throughctx.tracker.content. Content quests imply one completion per item. Replace retired content admission and approval calls with quest outcomes, claims and tracker outcome retries.Upgrade steps: Content stages.
-
c832384: Replace raw authoring resource keys with typed quest, ledger, tracker, and group handles. Authoring constructors and nested budget, tier, milestone, and bonus definitions now use
key; ledger declarations publish from modules; point-ledger settings return typed references. Stored releases and management, Console, and participant wire IDs remain compatible. See Authoring references use typed handles in the v1 upgrade guide. -
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
-
ae047ca: Remove the pre-v1 runtime, release, single-wallet and tracker compatibility shims. Published quests must carry an explicit evaluator protocol; protocol 1 remains supported, while protocol-less bundles must be republished. Participant and management reads now expose only canonical wallet lists, and tracker policies use canonical identity roles and an explicit observation mode. See Wallet reads return every linked wallet in the v1 upgrade guide.
Minor Changes
- b0bffa0: Add native distinct-action progress counters with calendar and rolling windows, durable achievements, participant summaries and audited operator correction. Participant state follows settlement order for late-arriving repeat batches, and achievement alerts remain scoped to their calendar window when the next window reopens. Add qualified-referral and continuous-membership examples. See counter rules. Update custom attempt renderers using the counter upgrade steps.
- 71cf912: Add reviewable leaderboard settlements. Fixed and calendar earned/net leaderboards can declare point and tiered-reward prizes, choose shared-rank or earliest-score tie handling, and freeze each ended window with its eligibility and exclusions at cutoff. Entries received after the cutoff stay out even when backdated, and a definition with a changed window advances past an obsolete scheduled cutoff. Operators review and approve delivery in Console or with
management.leaderboards, where the approval reason remains visible, and can correct an individual winner through completion revocation without awarding that settlement again. - 8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
- 11ab1d8: Add explicit archive and restore for code-declared resources through the management client, durable CLI operations and Console. Keep resource identities and history while stopping new activity, and preserve archive state across publication. See the resource lifecycle.
- 0a89168: Operators can archive point ledgers and correct points from Console member detail.
management.pointLedgers.list()includes nullablearchivedAt, andmanagement.pointLedgers.archive(id)archives a ledger. Archived ledgers remain in balance and history reads and existing published rewards keep working, but new reward pickers and published definitions reject them.
Patch Changes
-
ed4988c: Read Cosmos Hub and Osmosis bank balances with
ctx.wallet.balance(), pinned to one verified block and the member's wallets on the exact Cosmos network. -
2f78766: Fix
domino checkanddomino migratewhen a valid manifest omits either discovery array. Omitted arrays use the same empty defaults asdomino build, and explicit resources remain included. -
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
-
Updated dependencies [ad91102]
-
Updated dependencies [4b10080]
-
Updated dependencies [4205914]
-
Updated dependencies [6d66f96]
-
Updated dependencies [ed4988c]
-
Updated dependencies [b0bffa0]
-
Updated dependencies [bac9ca4]
-
Updated dependencies [84feaf7]
-
Updated dependencies [6cd0ba7]
-
Updated dependencies [3a38259]
-
Updated dependencies [71cf912]
-
Updated dependencies [6c30583]
-
Updated dependencies [6c30583]
-
Updated dependencies [c832384]
-
Updated dependencies [2528cec]
-
Updated dependencies [8a8870e]
-
Updated dependencies [11ab1d8]
-
Updated dependencies [ae047ca]
-
Updated dependencies [0a89168]
-
Updated dependencies [e1e701b]
-
Updated dependencies [1e23d8f]
- @domino-sdk/relay@1.0.0-next.34
1.0.0-next.33
Major Changes
- 89ea6ba: Add reviewed live project publication. The management client can list deployment records, submit and reject previews, promote a published test deployment, and prepare a rollback to the previous live deployment. Promotion and rollback accept an action UUID, and the management client generates one when omitted so retries return the same deployment record.
domino deploy --environment livenow returnsstatus: "pending", and--promotesubmits test source for live review; only a person in Console can approve, reject, or prepare rollback. The newpendingdeploy status is a breaking addition for exhaustive automation. See Live deploys return a pending review.
Patch Changes
- Updated dependencies [89ea6ba]
- @domino-sdk/relay@1.0.0-next.33
1.0.0-next.32
Patch Changes
-
6c30583: Add member-only activity pages,
me.activity.observe()anduseActivity({ onEvent })for committed asynchronous outcomes. Cursor reads preserve concurrent commits, and observers pause while hidden and reset on session changes. Events begin after the additive activity-feed migration; existing snapshots remain authoritative for earlier state. -
6c30583: Expose participant earned rewards through
rewards.entitlements()anduseEntitlements(), and include entitlements in experience reads. Addrewards.deliveries()anduseDeliveries()for points, tiers and integration deliveries. Preserve unknown delivery providers, kinds and states in reads while keeping backend delivery writes validated. Role displays use current reconciliation state after revocation.Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
-
Updated dependencies [6c30583]
-
Updated dependencies [6c30583]
- @domino-sdk/relay@1.0.0-next.32
1.0.0-next.31
Minor Changes
-
569989e: Reconcile Discord role membership across projects and environments, preserve external assignments, and expose operator recovery plus current membership alongside delivery history. Revoking the last accepted grant now removes Relay-owned assignments. Read the SDK v1 upgrade guide before upgrading.
Treat forbidden Discord membership reads as permanent delivery failures so operators can fix permissions and retry immediately.
Publish initial pending delivery events in the grant transaction so observers can see queued rewards before reconciliation starts.
Patch Changes
- Updated dependencies [569989e]
- @domino-sdk/relay@1.0.0-next.31
1.0.0-next.30
Minor Changes
-
afe1128: Operators can revoke an accepted completion with a reason:
management.completions.revoke({ completion, reason, freeOccurrence })(POST /management/v1/completions/<id>/revoke,reviewpermission). In one transaction the completion stops counting (progress, prerequisites of new attempts, counts, profiles and referral qualification), its point awards are reversed as far as the member's available points allow, tiered rewards it granted are voided or lowered to the tier another completion grants (collected ones are flagged), Discord role grants not yet delivered are stopped while delivered roles are flagged for the operator to remove in Discord (Relay never removes roles; automatic removal is DOM-99), referral programs that qualified on it are re-evaluated, and a streak milestone it paid is revoked while its days stay.freeOccurrencelets the member complete the quest again for the same completion key; by default later attempts for it are rejected. The result reports each part of the cascade. API keys withreviewpermission can revoke in live. Console offers it on member detail and accepted attempts, and the attempt history and Audit history show the revoke and its reason.domino capabilities --jsonnow markscompletion-revokeas native.Breaking: a reward delivery's
actionis an open string (RewardDeliveryActionlists"grant-role"), the worker fails a delivery whose action it does not know, and a grant still pending at the revoke fails. The revoke result'soutcomefields are open strings with known values inRevokedEntitlementOutcome,RevokedDeliveryOutcomeandRevokedReferralOutcome. To upgrade, see Reward delivery actions are open strings.Revoking preserves percentage bonuses from other valid completions and queues a new automatic attempt when a freed occurrence still meets its prerequisites. Delivery responses add optional
dispatchedAtandrevokedAtfields. Dispatch history survives retries, revoke fences failed generations, and late provider success stays visible without reopening delivery. Console uses the revoke field to stop retries and shows unknown delivered actions by their generic status.Revocation refuses missing or unreadable grant history before changing member state. Collected entitlements carry a persistent recovery flag in ordinary reads and Console. Correcting collection clears the flag and voids an unearned reward instead of reopening it; a surviving grant reopens only its earned tier. Reused-completion attempt timelines include the shared completion’s revoke event.
Patch Changes
- Updated dependencies [afe1128]
- @domino-sdk/relay@1.0.0-next.30
1.0.0-next.29
Minor Changes
-
67d0d8c: Add bounded
decision.deferand absolutedecision.progressresults, fresh evaluation runs scheduled by durable timers, participant progress and deferred status, and consistent completion-key authoring types. Reviewers can reject a deferred attempt to stop it.Attempt response statuses accept future server values; use known
AttemptStatusconstants with a fallback. New manual submissions while deferred return409 attempt-pending. Custom decision/status switches must handle the new variants. See Handle deferred evaluator decisions and progress for old and new code, capture semantics, and search patterns. This ships in the1.0.0-nextprerelease series. -
d3b6261: Restrict personal ingestion keys to selected custom trackers, inspect and edit their scope without changing the secret, and return TRACKER_SCOPE_DENIED for disallowed writes. Human CLI sign-ins can manage their own keys; project-scoped playground sessions and API keys cannot. Existing unrestricted keys retain project-wide ingestion.
Patch Changes
- Updated dependencies [67d0d8c]
- Updated dependencies [d3b6261]
- @domino-sdk/relay@1.0.0-next.29
1.0.0-next.28
Patch Changes
- 83c4426: Update Discord message tracker records when a message is edited or deleted. Turning text off or deleting a message now erases retained message text.
- Updated dependencies [83c4426]
- @domino-sdk/relay@1.0.0-next.28
1.0.0-next.27
Minor Changes
- e7f81ff: Add signed outbound webhooks for operator backends, with scoped subscriptions, bounded retries, delivery logs, replay, and overlapping secret rotation. Expose
management.webhooks,verifyWebhook, shared activity constants, anddomino webhookscommands. This is additive; existing integrations need no changes. See Receive outbound webhooks for an optional polling migration.
Patch Changes
- Updated dependencies [e7f81ff]
- @domino-sdk/relay@1.0.0-next.27
1.0.0-next.26
Minor Changes
- 8c06912: Add operator reads and audited unlink for current X and Discord quest accounts. Reviewers provide the subject and selection revision to prevent a stale review from unlinking a newer connection. Identity ownership, sign-in grants and existing rewards remain unchanged. Console exposes the action on each member's Quest accounts section.
Patch Changes
- Updated dependencies [8c06912]
- @domino-sdk/relay@1.0.0-next.26
1.0.0-next.25
Minor Changes
- 903ffad: Persist recoverable CLI mutations before sending and add
operations list,showandresume. Creation resumes through repository and checkout failures; quest lifecycle changes retain their action ID and scoped input. Errors for known partial failures may now include an operation to resume. See the saved CLI mutation recovery upgrade guide.
Patch Changes
- @domino-sdk/relay@1.0.0-next.25
1.0.0-next.24
Major Changes
-
2528cec: Every tracker trigger shares the admission rules of ADR 0006: enrollment, the earliest activity counted, and who releases historical activity, with skips decided again when the member event that clears them happens.
- Admission options on
automatic({ tracker }).enrollment: "required"counts only members who enrolled in the quest, andctx.tracker.enrolledAtcarries their enrollment time.sinceis"activation"(the default),"window", or, with required enrollment,"enrollment".historicalis"staff"(the default),"claim"or"automatic". - Historical activity waits. A record from before the member linked the account it pays, or with an unknown link time, is skipped as
historicaluntil a reviewer retries it, unless the quest setshistorical: "claim"or"automatic". Before, it was admitted. See Tracker triggers hold activity from before an account was linked. - Skips are decided again by member events. Linking an account, removing a conflicting link, joining the project and enrolling decide the matching skips again, through a durable workflow.
- Every selected observation has one outcome. New skip codes
outside-window,future-record,already-completed,not-enrolled,before-enrollmentandhistorical;TrackerSkipCodeis an open string, withKnownTrackerSkipCodefor the codes this SDK knows. A prerequisite completed after the record occurred no longer counts for it. - Participant APIs.
relay.quests.enroll({ actionId, quest }),relay.quests.outcomes({ quest?, state?, code?, cursor? })andrelay.quests.claim({ actionId, quest, tracker, record, observedAt }), anduseQuestOutcomes()in React.relay.content.enrollandmanagement.content.enrollare replaced byrelay.quests.enrollandmanagement.quests.enroll. See Enroll in quests withrelay.quests.enroll. - Console. A tracker-triggered quest shows its enrollment,
sinceand historical policy, anot-enrolledskip offers Enroll the member, and a member's claim shows as Claimed.
Who should act. Apps that enroll members or read trigger outcomes, and quests whose members link accounts after the activity they expect to be paid for.
- Admission options on
Patch Changes
- Updated dependencies [2528cec]
- @domino-sdk/relay@1.0.0-next.24
1.0.0-next.23
Major Changes
- 2149973: Read published trackers with future source kinds without rejecting a tracker list. Preserve unknown source payloads and expose
isKnownTrackerSourcefor supported-source narrowing. Tracker authoring remains closed. Update code that assumes every response source is supported as described in the v1 upgrade guide.
Patch Changes
- Updated dependencies [2149973]
- @domino-sdk/relay@1.0.0-next.23
1.0.0-next.22
Major Changes
-
4626004: Identity kinds are open. Besides
wallet,x,discordandmember, a custom tracker can declare your app's own kinds,app:<name>such asapp:telegram, and records name your app's user IDs. An identity integration created withidentityKind: "app:<name>"(set only at creation, kept by rotation) links each subject it signs in to the member as an identity of that kind, and tracker triggers attribute records through it, with{ provider: "app:<name>", subject, revision, linkedAt }asctx.tracker.attribution.account.A kind belongs to the integration that first claimed it, also after a disconnect, which keeps its links attributing. A new integration takes a kind that has identities only by adopting them:
adoptIdentitiesFrom(the old integration's ID, from the newmanagement.identityIntegrations.kinds()) with areasonthe audit log keeps; otherwise it is refused with 409. Reviewers unlink and relink a member's app identity (management.members.unlinkIdentityandrelinkIdentity, and Console's Tracker identities), each audited with a reason: unlinking stops attribution, ends the member's sessions for that subject, and refuses its sign-in with 409APP_IDENTITY_UNLINKED, whichcreateParticipantBackendpasses on, until it is relinked. A subject the integration already signs in as another member answers 409APP_IDENTITY_CONFLICT.Evaluators read the member's selected X and Discord accounts and app identities with
ctx.member.identities(), pinned once per attempt andnullwhen there are none. Management member reads return them asidentities, and Console lists them on the member under Tracker identities.Published tracker definitions in status, list, record-page, summary, and deployment responses use
trackerDefinitionResponseSchemaandTrackerDefinitionResponse. Authoring keeps the stricttrackerDefinitionSchemaandTrackerDefinition.Responses and evaluator contexts read identity kinds as an open set,
OpenIdentityKind, and ignore fields they do not know, so a kind a later Relay adds no longer fails them.Breaking:
IdentityKindis no longer a closed union,identityKindSchemais no longer az.enum, response kinds areOpenIdentityKind,trackerRecordSchemais a separate response schema,trackerAttributionSchema's account is a plain union with app and generic variants, andctx.memberis{ id, identities() }. See Identity kinds are open, and evaluators readctx.member.identities().
Patch Changes
- Updated dependencies [4626004]
- @domino-sdk/relay@1.0.0-next.22
1.0.0-next.21
Minor Changes
-
ddfc526: The participant client, the management client and the CLI share one set of deadline, cancellation and retry rules.
- Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
createRelayClient({ timeout })and the new per-call{ timeout }option of every participant method set it, astimeoutalready does for the management client. A timeout is never retried. - A call is sent again, at most three times in all, only when the server applies a repeat at most once: reads; participant writes with an action ID, submission evidence by its upload ID,
wallets.verify(by its challenge) andwallets.challenge, but not avatar uploads, whose upload ID has no receipt; typed management writes whose input has anactionId; and the CLI'sdeploypublication,stagebuild request andquests archive/unarchive. Every attempt sends the same payload and session. Retries follow a lost answer, including a body that breaks off after successful headers, and HTTP 500, 502, 503 and 504, with a short jittered backoff. Invalid or unexpected answers, cancellations, rejected input, other refusals and every other write are sent once. - A 429 is retried only when its
Retry-Afterfits in the time left, and every wait honoursRetry-After. The newretryAfterfield ofRequestError(and soRelayErrorandManagementError) carries the wait the server asked for, in milliseconds; the CLI reports it asdetails.retryAfteronRATE_LIMITEDandSERVER_ERROR. - A session-bound participant call that reads the current session first shares one deadline with that read, and
auth.session()honourstimeout. - An error status whose body breaks off, times out or is cancelled is now a
network,timeoutorabortedfailure that keeps itsstatusandretryAfter, in all three clients; an error body that is not JSON stayshttp. - Cancelling with
signalstops a retry wait at once. A write any attempt of which may have been applied staysuncertain, with itsoperationIdandrecovery, whatever later attempts answer. management.requestretries only GET, andmanagement.rawsends once.useInvitation()keeps a rate-limited code for a retry instead of discarding it as refused.
Avatar failures report
recovery: "reconcile"even when they have an upload ID. Wallet address and signature waits reject when the caller cancels. See Bounded requests and avatar recovery for the recovery-handler change.Generic management writes report
recovery: "reconcile"even with a caller-supplied action ID. Uncertaindomino apiwrites exposedetails.recovery: "reconcile". See Generic management writes require reconciliation for the recovery-handler change. - Each call has one deadline covering every attempt, the waits between them and the answer's body: 15 seconds for the participant client and 30 seconds for the management client and the CLI.
Patch Changes
- Updated dependencies [ddfc526]
- @domino-sdk/relay@1.0.0-next.21
1.0.0-next.20
Minor Changes
-
d1ef769: Relay answers 503 only when it provably did nothing, so a write it reports as failed with 503 is safe to send again under a new action ID:
- An unexpected server error is 500, not 503. It can follow a commit, and the SDK, the management client and the CLI already treat 500 on a write as
uncertain. - A lost or unusable answer from an internal service is 502, or 504 when it timed out, where it was 503: publication, the Discord integration service, the preview service, Clerk API key changes, and the cloud builder ("Could not start cloud build. Retry with the same request ID." is now 502 or 504).
- 503 stays for refusals before any work: missing configuration, a dependency check that fails before the transaction, and a data space closed for maintenance, which now sends
Retry-After: 60. - Rate-limited answers (429) send
Retry-Afterwith the seconds until the limit's window resets: sign-in starts, email codes, wallet sign-in and wallet linking, CLI device login and approval, identity exchange, profile and avatar changes, playground starts, preview starts, development keys, tracker previews and feedback.
The
participant-apiandhostedagent references describe the 503 rule and the 502 or 504 build start.Upgrade custom retry handling using Treat failed writes as uncertain when Relay cannot prove refusal. Evidence and avatar PUTs, email delivery, Clerk cleanup, and activation failures after a commit now preserve uncertainty. Staging password limits also carry
Retry-After. - An unexpected server error is 500, not 503. It can follow a commit, and the SDK, the management client and the CLI already treat 500 on a write as
Patch Changes
- @domino-sdk/relay@1.0.0-next.20
1.0.0-next.19
Patch Changes
- Updated dependencies [1141af4]
- @domino-sdk/relay@1.0.0-next.19
1.0.0-next.18
Minor Changes
-
8465d40: Operators can correct a member's tiered reward, at the revision they saw and with an audited reason.
management.handover.cancel(entitlement, { revision, reason })(POST /management/v1/entitlements/<id>/cancel,handoverpermission) returns a handover in progress toavailableand puts its pickup size back into stock.management.entitlements.correctCollection(…/correct,review) does the same for a collection recorded by mistake, andmanagement.entitlements.void(…/void,review) voids an available entitlement. A stale revision fails with 409entitlement-changed, the wrong starting status with 409entitlement-status. API keys with the permission can make these corrections in live. Console Rewards and member detail offer them, and Audit history lists each one with its reason.domino capabilities --jsonlists them asentitlement-corrections.Breaking:
Entitlementgains the status"void", withvoidedAt. A voided entitlement cannot be collected; a quest that grants the same reward to the member again makes itavailable, as a fresh grant at that quest's tier; a voided referral reward stays void. SDKs from before this release fail the whole progress read once a member has a voided entitlement. Reads now parse entitlements with a tolerantentitlementSchema(fields a newer server adds are kept, and an unknown status reads as"unsupported"withreportedStatus); the strict stored shape isentitlementStateSchema, and the management client'sentitlementRecordSchemais removed in favor ofentitlementSchema. To upgrade, see Entitlements can bevoid, and reads tolerate statuses they do not know.
Patch Changes
- Updated dependencies [8465d40]
- @domino-sdk/relay@1.0.0-next.18
1.0.0-next.17
Patch Changes
- Updated dependencies [c919948]
- @domino-sdk/relay@1.0.0-next.17
1.0.0-next.16
Major Changes
-
bdb89a4: Evaluator readers follow one rule: a missing subject is
null, a failure throwsCapabilityError, linked accounts are pinned once per attempt, and tracker reads are point in time. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.- Missing is
null.ctx.x.profile(),follows()andreposted()returnnullwithout a linked X account instead of throwing;ctx.x.post()returnsnullfor a post that does not exist and needs no linked account.ctx.discord.member()returnsnullwithout a linked Discord account.ctx.wallet.list()returnsnullinstead of[], andctx.wallet.balance()returnsnullinstead oftotal: "0"when the member has no wallet of that family.ctx.trackers.content()returnsnullfor a missing record or observation. See Readers returnnullwhen an account is not linked or a subject is missing. - Failures throw
CapabilityError, new in@domino-sdk/relay/authoring(its wire shape,capabilityFailureSchema, is in@domino-sdk/relay/schema), withcode("not-linked","provider-unavailable","author-error") andretryable. Invalid capability input isauthor-error; an image check of{ x: "profile" }without a linked X account isnot-linked. Failures that are not retryable are captured and replay. A retryable failure that quest code handles and moves past is final for the attempt; one that escapes the evaluation is released, along with every fact captured after it, and retried, so the retry asks the provider and the later calls again. Rethrow a caught failure, or wrap it with{ cause: error }, to let it be retried. The failure shape is open: a newer Relay can add codes, and the SDK treats an unknown code as not retryable. See Capability failures throwCapabilityError. - Tracker reads are point in time.
ctx.trackers.records()andsummary()takeasOf, defaulting toctx.eligibleAt, and every tracker read of an attempt sees the observations stored at its first tracker read. PassasOf: ctx.nowto count everything stored by then. See Tracker reads are point in time and pinned per attempt. - Linked accounts stay pinned. X and Discord readers pin the account at the attempt's first read and never recheck it, as wallets already did, so a link change no longer fails the attempt with "X account changed" or "Account or integration changed". See Linked X and Discord accounts stay pinned for the attempt.
Who should act. Evaluators that read
ctx.x,ctx.discord,ctx.walletorctx.trackers, or that match capability error messages. The changed return types point TypeScript at each read to update. Thediscord-membership,token-holderandcommunity-screenshotsnippets and the Discord starter'sjoin-the-serverquest are updated. - Missing is
Patch Changes
- Updated dependencies [bdb89a4]
- @domino-sdk/relay@1.0.0-next.16
1.0.0-next.15
Major Changes
-
e1cba88: Observations are removed, and every automatic activity arrives as a tracker record.
automatic({ source, type }),ctx.observation,management.observations.submit,POST /management/v1/observations, theObservationtype,observationSchema,observationInputSchema,observationReceiptSchema,observationTriggerSchema,discordReactionSchema,QuestView.trigger.observation(and the release trigger'sobservation) and theobservationfield of theautomaticavailability are gone.questStatusandrunsSilentlyfrom@domino-sdk/relay/reactnow label every tracker-triggered quest "Automatic", not only observation and keyed quests. A server that reported observations sends records to a custom tracker withmanagement.trackers.ingestand triggers quests withautomatic({ tracker }). Ingestion never joins a member to a project: a tracker trigger skips a member who has not joined withnot-joined. Quests published with an observation trigger leave the active set when Relay upgrades. Republish them from code on a tracker; the quest key can stay the same.Discord messages and reactions are two managed tracker sources,
discord.messages()(with{ text: true }to store message text) anddiscord.reactions(), on thediscordobject from@domino-sdk/relay/authoring. Domino's Discord gateway writes each new message or reaction into every tracker of that kind in the projects connected to the server, and keeps a record only when one of its accounts is a Discord account a member has linked. Their record data is typed (DiscordMessageData,DiscordMessageTextData,DiscordReactionData), sowherefilters andctx.tracker.record.dataare checked. A reaction record names the message's author as themessageAuthoraccount:automatic({ tracker: reactions, beneficiary: "messageAuthor" })replacesreaction.received, and Relay skips reactions to one's own message withbeneficiary-is-author. Discord trackers refuse ingestion (403) and refresh (422). Streaks work on these triggers.The
observepermission is removed. Ingesting needsingest, and enrolling a member from a server (management.content.enroll) needs the newenrollpermission. Editors holdread,publish,inventory,points,ingestandenroll; hosted-app keys holdread,pointsandingestand cannot enroll. A personal API key issued withobservekeeps its other permissions, andobservegrants nothing: reissue it with Ingest or Enroll. Theobservation-questscapability is removed fromcapabilities.json; Discord tracker triggers are part of thetrackerscapability.To upgrade, see Observation triggers become tracker triggers and The
observepermission is replaced byenroll.
Patch Changes
- Updated dependencies [e1cba88]
- @domino-sdk/relay@1.0.0-next.15
1.0.0-next.14
Patch Changes
- Updated dependencies [c2390b3]
- @domino-sdk/relay@1.0.0-next.14
1.0.0-next.13
Major Changes
-
23c85ba:
defineEngagementRewardtakesbudgets, a list of{ scope: "project" | "member", period: "day" | "week" | "month" | "lifetime", limit }, instead ofbudget: { projectDaily, memberDaily, projectLifetime }. Periods are those of the project calendar at settlement time, so a rule can now cap weekly or monthly payouts, and accounts settle once per project calendar day instead of once per UTC day. A definition withbudgetfails validation; rules already published keep their limits in the new form.See Engagement budgets are a list of periods in the v1 upgrade guide.
-
23c85ba: Each project environment has one project calendar: a timezone, UTC until you choose one, that sets where every day, week and month begins. Streak days, quest budgets, engagement budgets and calendar leaderboard windows all follow it, so a weekly budget and a weekly leaderboard cover the same week.
- Set it with
"calendar": { "timeZone": "Europe/Budapest" }in relay.json, in Console Settings → General → Project calendar, or with the newmanagement.calendar.read()andmanagement.calendar.configure({ timeZone, reason })(GETandPOST /calendar,publishpermission; in live only from Console). A change takes effect at the next midnight in the current timezone: the open day, week or month finishes in the old zone, and streaks stay consecutive. A deployment applies relay.json's timezone only when it differs from the last one deployed, and the deployment preview's newcalendarfield shows the change it schedules. streak()and calendar leaderboard windows no longer take atimeZone; both throw if given one. Streaks and boards already published with one still read.- Budgets reset at midnight in the project timezone instead of UTC, and accept
period: "month". - One validator,
timeZoneSchema, accepts named IANA zones only. Fixed offsets such as+02:00andEtc/GMT-2are rejected, including in leaderboards, which used to accept them. The sharedcalendarPeriodSchemaislifetime | day | week | month.
Who should act. Projects that pass
timeZonetostreak()or a calendar leaderboard window, and projects that rely on budgets resetting at UTC midnight. See Set the timezone once, on the project calendar in the v1 upgrade guide. - Set it with
Patch Changes
- Updated dependencies [23c85ba]
- Updated dependencies [23c85ba]
- @domino-sdk/relay@1.0.0-next.13
1.0.0-next.12
Patch Changes
-
710e520: Participants read their own point balances and history.
relay.me.balances()(GET /v1/me/points) returns{ items }: the signed-in member'sbalanceandavailablepoints (after reservations) in every ledger of the project, each with itsledgerreference.relay.me.pointHistory({ ledger, cursor? })(GET /v1/me/points/:ledger/history) pages through the member's own entries in one ledger, by key or ID, most recently recorded first, as{ items, nextCursor }. Each entry is{ id, amount, kind, at, quest, reverses }, without the operator's reason or actor.kindis an open string; thePointEntryKindconstants (award,spend,adjust,reverse) name the kinds this SDK knows, and referral payouts areadjust. An unknown ledger answers 404 with codepoint-ledger-not-foundand a member who has not joined the project 404 withnot-joined, both added toRelayErrorCode. Neither read takes a member, and both are scoped to the session's project and environment. React apps useuseBalances(), which reads again after a quest completes, andusePointHistory(ledger). The participant proxy and identity integrations admit both routes. New schemas and types:pointBalanceSchema,pointBalancesSchema,pointEntrySchema,pointHistoryQuerySchema,pointHistoryPageSchema,PointBalance,PointBalances,PointEntry,PointEntryKind,KnownPointEntryKind,PointHistoryQueryandPointHistoryPage.The classic starter's header now reads the balance with
useBalances()instead of its/api/me/pointsserver route, which is gone. -
Updated dependencies [710e520]
- @domino-sdk/relay@1.0.0-next.12
1.0.0-next.11
Minor Changes
-
f2f1b75: Breaking: the management client moves from
@domino-sdk/relay-clito the new@domino-sdk/relay/serverentrypoint, withcreateManagementClient,hostedManagementClient,ManagementError, the management schemas and types, andstarterTemplates. Server code, including a hosted app's Worker, now needs only@domino-sdk/relay, without the CLI's command-line parser and TypeScript compiler.@domino-sdk/relay-cliis only thedominoexecutable and its agent skills; it has nothing to import.serverBoundary()also fails a browser build that imports@domino-sdk/relay/server. To upgrade, see Import the management client from@domino-sdk/relay/server.The capability catalog (
domino capabilities --json) adds staff confirmation, staff handover, stock, point corrections, deploy and stage, operator member reads and lookup, project access, and the management API withdomino api. It marks webhooks, bulk data export, completion revocation and tracker-scoped keys as unsupported, and lists the management methods it leaves out on purpose inexcludedManagementMethods.
Patch Changes
- Updated dependencies [f2f1b75]
- Updated dependencies [909f343]
- @domino-sdk/relay@1.0.0-next.11
1.0.0-next.10
Minor Changes
-
0052894: The management client has typed methods for the routes Console and scripts called by path:
attempts.confirm,attempts.reviewandattempts.retry,passes.verify,handover.beginandhandover.confirm,operations.configure,inventory.addStock,quests.updateSettings,tokens,grants,teamandidentityIntegrations. The newmembers.lookup({ provider, subject })(GET /management/v1/members/lookup,readpermission) finds the one member of the project holding exactly that account, and fails with 404member-not-foundor 409account-ambiguous.Breaking: every failed call rejects with a
ManagementErrorwhosekind(http,network,timeout,aborted,invalid-response,client) separates a server refusal from a lost or unreadable answer and from input rejected before sending. It keeps the server'smessageand anycode, including codes this version does not know, and reportsuncertain, withoperationId(theactionIdit sent) andrecovery, when a write may have been applied. It extends the SDK's sharedRequestError;isManagementError()recognizes it.CreationErroris removed. Calls end after 30 seconds by default; every method takes{ signal, timeout }as its last argument, and the client takes a defaulttimeout. To upgrade, see CatchManagementErrorinstead ofCreationErroror a status property.Breaking:
member,people,unlinkMemberWalletandsetMemberWalletSignInmove tomembers.get,members.people,members.unlinkWalletandmembers.setWalletSignIn. See Member methods moved undermanagement.members.
Patch Changes
- @domino-sdk/relay@1.0.0-next.10
1.0.0-next.9
Minor Changes
-
cf5b02c: Custom tracker records have a declared data schema.
customSource(namespace, { data })requires a zod object schema (zis exported by@domino-sdk/relay/authoring), stores its JSON Schema form with the tracker, and throws, naming the field, for what ingestion cannot check (string formats and patterns, tuples, refinements, transforms, dates, bigints). Publication refuses a custom tracker without one. Ingestion rejects a batch whose record data does not match with 422 and{ error, code: "invalid-record-data", record, path, reason }, one too costly to validate withrecord-data-too-complex, and records for a tracker published without a schema withtracker-schema-required. Schemas are bounded (32 KB, 16 levels, 256 properties, required keys, enum values or key names per node, 16 branches per union, no wide union nested in another union's branch), and validation charges every unit of work to a per-record and per-batch step budget. A deployment whose new schema breaks a live quest'swherefilter, Console quests included, is refused. Stored records are not rechecked: tracker triggers skip non-matching records with the new skip codeinvalid-record-data, evaluators reject pinned records that do not match the schema the quest was built with, andtrackers.records(handle)leaves them out and counts them inexcluded. The schema can change under the same tracker key and applies to later records.The data type flows into quests:
ctx.trackeris non-null in a tracker-triggered quest's evaluator withrecord.dataof the tracker's type (andctx.contentandctx.observationare non-null for the triggers that supply them,nullotherwise),trackers.records(tracker)returns typed records, andwherepaths,equalsvalues and{ setting }bounds are type-checked against the data and the quest's integer settings.wherealso accepts a list of conditions that must all hold, andautomatic()and publication check filter paths against the stored schema.settings.objectanddefineQuestaccept onlysettings.*builders; a plain zod setting is a type error anddefineQuestthrowsSetting "<key>" is not a quest setting.See Custom sources declare their record data and Quest settings come only from settings builders in the upgrade guide.
Patch Changes
- Updated dependencies [cf5b02c]
- @domino-sdk/relay@1.0.0-next.9
1.0.0-next.8
Major Changes
-
54fcf41: The participant client follows one convention for paging, sessions and errors.
- Every method takes an optional last argument,
{ session?, signal? }.sessionreplaces the positional session argument ofwallets.challenge,wallets.verifyandme.collectionPass. Calls Relay binds to a session (wallet linking, collection passes,quests.retry) read the current session whensessionis omitted. - Every paged read takes
{ cursor? }and returns{ items, nextCursor }:referrals.history,content.admissions,content.qualifications,content.performance,engagement.pageandengagement.boundarieschanged frombefore,afterandnext. The participant routes answernextCursorand readcursor, and so do the management routes and client methods that share them (management.referrals.history(member, { cursor }),management.content.*,management.engagement.pageandboundaries). - Every rejection is a
RelayError, which extends the new sharedRequestError:kind(http,network,timeout,aborted,invalid-response,client),uncertain(true only for a write that may have taken effect: a failure after sending, a cut-off body, a 500, 502 or 504, or an unreadable success answer),statuswhenever response headers arrived, an open stringcode(known ones inRelayErrorCode),operationId, andrecovery(replaywith anoperationId, elsereconcile) when uncertain.isRelayErrorandisRequestErrorrecognize these errors across duplicate copies of the package.RelayError.statusis nownumber | undefined. Invalid input rejects withinvalid-inputinstead of aZodError, a success answer this SDK cannot read withinvalid-response, a body cut off after its headers with kindnetworkand its status, and a wallet signer's own failure withwallet-signer. The error body is read field by field, so Relay's message and a code the SDK does not know are always kept. Relay now sendscode: "not-signed-in"andcode: "stale-session"on those participant errors, and every paged read answers a malformed cursor with 400, codeinvalid-cursor, and one message. - Removed:
submit,retry(id),progress(member),upload(member, file),auth.connections,me.linkPass, thewallet.*namespace, and the staff calls the participant API refused (lookupMember,snapshot,publish,publishBatch,beginHandover,confirmHandover,decide,journeyTemplate,template). - Parameters take Zod input types, so defaulted fields are optional, and cursors are opaque strings the server validates. The session the client returns (
ParticipantSession) accepts fields and principal kinds a newer Relay adds;sessionMember(session)reads its member. - Hosted proxies no longer forward the 0.x single-wallet routes (
/v1/me/wallet,/challenge,/verify).
Upgrade: One paging, session and error convention in the participant client in the SDK v1 upgrade guide maps every method and has search commands.
- Every method takes an optional last argument,
Patch Changes
- Updated dependencies [54fcf41]
- @domino-sdk/relay@1.0.0-next.8
1.0.0-next.7
Patch Changes
-
1084288:
@domino-sdk/relaynow exports only the participant runtime client:createRelayClient,RelayError,QuestUpdatedError,formatUnits,parseUnits, and the types the client returns. Schemas, builders and avatar helpers moved to their own entry points, a few implementation helpers are no longer public, and two are no longer exported at all, so an app that imports the client no longer loads authoring builders, deployment and management schemas, or DiceBear.- Zod schemas (and helpers such as
identifier,caip10andxPostId) come from@domino-sdk/relay/schema. - Builders (
defineTracker,defineLeaderboard,defineReferral,defineEngagementReward,content,perContent,streak,settings,discord,pointLedgers,x,customSource) and evaluator reader types (TrackerReader,XReader,ImageReader) come only from@domino-sdk/relay/authoring. avatarSrc,defaultAvatar,defaultAvatarSvgandwalletAvatarSeedcome from@domino-sdk/relay/avatar, and no longer from@domino-sdk/relayor@domino-sdk/relay/react.- No longer public:
identityIntegrationSchema,identityIntegrationInputSchema,identityIntegrationSecretSchema,participantExchangeSchema,questConfiguration,parseImageResult,parseImageExtraction,isContentParticipantRoute,isProfileParticipantRoute,isWalletParticipantRouteandisWalletSignInRouteare exported only from@domino-sdk/relay/internal, which Relay's own services share and which is not a supported API. - No longer exported:
questActionsandreconcileQuestDraft. Open a quest controller withrelay.quests.open(view); itsquest-updatedresult already carries the reconcileddraft. - The package declares
sideEffects: false. esbuildis an optional peer dependency, needed only by@domino-sdk/relay/build.@domino-sdk/relay-clidepends on it directly, sodomino buildanddomino deploywork as before. Code that callsbuildQuestwithout the CLI must installesbuild.
Upgrade: Runtime entry exports only the client in the SDK v1 upgrade guide has the import mapping and search commands.
- Zod schemas (and helpers such as
-
Updated dependencies [1084288]
- @domino-sdk/relay@1.0.0-next.7
1.0.0-next.6
Patch Changes
- 1019218: Console Audit history now records every point ledger created in Console or with
management.points.createLedger, as Point ledger created, with the ledger's key and name, the environment and who created it. Before, only edits were audited. - Updated dependencies [8b706af]
- @domino-sdk/relay@1.0.0-next.6
1.0.0-next.5
Major Changes
- fa16503: The CLI's automation contract is frozen for v1. Every failure has a stable error
code, and the exit status says what kind of failure it was: 0 success, 1 failure, 2 usage error (was 1), 3 a change that may have taken effect without confirmation (was 1), and 130 cancelled by SIGINT or a prompt, 143 by SIGTERM (was 1). With--json, the error on stderr's last line always haserror,codeandoutcome, plusstatus,apiCode,detailsoroperationwhen they apply; Commander'scommander.*codes are gone. An uncertaindeploypublication orstagebuild reports anoperationwhoseresumearguments pin the full scope and finish it from any directory, announced as a JSONoperationevent on stderr right before the request is sent.deploy --resumeandstage --request-idnow require that scope as flags and need no checkout.doctor,checkandmigratealso write their failure to stderr. The undocumenteddomino GET /pathspelling is removed; usedomino api GET /path.domino helpwith an unknown command fails withUNKNOWN_COMMAND.domino devis now listed under Experimental commands. Result and error JSON Schemas, fixtures, a command inventory andcli/contract/index.mjs(the zod schemas by name) ship incli/contract/, anddomino reference automationexplains them. To upgrade, see CLI errors have stable codes and exit statuses and Call the management API withdomino api.
Patch Changes
- @domino-sdk/relay@1.0.0-next.5
1.0.0-next.4
Minor Changes
- d7f9fe4:
streak(...)now completes manual quests and tracker-triggered quests (automatic({ tracker })), not only automatic observation quests. Each accepted attempt counts the community day of its trusted occurrence time, the timectx.eligibleAtreports: a tracker record observation'soccurredAt(such as an X post's publication time), or, for a manual quest, when Relay admitted the submission, so a manual streak counts submission days.defineQuestno longer throws "Streaks require an automatic observation trigger" or "Tracker triggers complete once() or keyed()", and publication accepts these quests. Content quests and prerequisite-onlyautomatic()quests still cannot be streaks, and a streak quest keeps its trigger kind (manual, observation or tracker): changing it needs a new quest key. Once a member's tracker streak milestone pays, the trigger stops admitting that member's observations for the quest, as for a completedonce()quest.questStatusshows a member's own streak submission first while it is checking, in review or failed; a rejected submission keeps the "N / M days" progress and gives the reason as its detail. The newpackages/sdk/examples/posting-streak-quest.tspays members who post on X five days in a row. Nothing existing needs to change, so the v1 upgrade guide has no section for this.
Patch Changes
- Updated dependencies [d7f9fe4]
- @domino-sdk/relay@1.0.0-next.4
1.0.0-next.3
Major Changes
-
a803b30: Evaluators now replay deterministically. Hosted evaluation freezes the clock for each attempt and seeds randomness from the attempt, so a retried or replayed attempt makes the same decision and
keyed()completion key. This applies to every evaluation Domino runs, including releases published with earlier SDK versions.ctx.nowis new: the attempt's evaluation time in epoch milliseconds, the same on every run of the attempt.keyed()receives it too. It is distinct fromctx.eligibleAt, which is when the activity happened.Date.now(),new Date()andDate()returnctx.nowand do not advance, even across awaited capability calls.performance.now()returns 0.Math.random(),crypto.getRandomValues()andcrypto.randomUUID()repeat the same sequence for one attempt and differ between attempts.
Who should act. Evaluators that measure elapsed time or rely on unpredictable random values. Use
ctx.nowfor "as of evaluation" checks andctx.eligibleAtfor when the activity happened. See Evaluators see a frozen clock and seeded randomness in the v1 upgrade guide.
Patch Changes
- bd960a4: Only a person signed in to Console can create or edit a point ledger in live.
management.points.createLedgerandmanagement.pointLedgers.savewith an API key or a CLI sign-in now get 403 "Approve live publication in the console" in live, as publication already did; in test they work as before. To upgrade, see test and live. Live operator controls stay open to a personal API key withpublish: operation pauses (POST /management/v1/operations),management.referrals.control,management.leaderboards.excludeandgroup, andmanagement.trackers.control. Console Audit history now lists leaderboard exclusion, group and tracker control changes with the key's owner as the actor. The agent references andcapabilities.jsondescribe both rules. - Updated dependencies [a803b30]
- Updated dependencies [7e37049]
- @domino-sdk/relay@1.0.0-next.3
1.0.0-next.2
Minor Changes
-
c320586: A content quest's
performance.evaluatecan no longer select rewards. It gates the rewards the content evaluation already selected, and it always did: arewardsvalue in its result was silently ignored. Its type is nowPerformanceOutcome(accept,revieworrejectwith a reason only), sodecision.accept({ rewards })anddecision.review({ reason, rewards })are type errors there, and a runtime result that carriesrewardsfails the evaluation with "A performance evaluator cannot select rewards". To change what a post earns, select rewards in the quest'sevaluate.See Performance evaluators no longer select rewards in the upgrade guide.
-
c320586: A prerequisite is the quest key alone.
completed(quest)returned{ quest, scope: "ever" }, andscopewas declared but never read: a prerequisite has always meant a required quest completion from any prior occurrence, with no same-period variant.completed(), the quest description schema (requires[]) and published releases no longer carryscope. Releases stored with it still read, and the field is dropped. Code that readsrequires[].scope, or builds{ quest, scope: "ever" }by hand, should use{ quest }orcompleted(quest).See Prerequisites are typed eligibility rules in the upgrade guide.
Patch Changes
- c320586:
defineQuestthrows at build time for two shapes that only failed when publishing: a bareautomatic()quest with norequiresentry, and a bareautomatic()quest withkeyed()completion. A bareautomatic()runs once its prerequisites are complete, so it needs at least onecompleted(quest)and completes once. The backend still checks both when it publishes. - c320586:
manual({ actor: "staff", input })throws instead of dropping the actor. Its types never allowed the combination, but untyped callers got a quest that members could submit evidence to, not one staff confirm. Staff-captured evidence is not supported: usemanual({ actor: "staff" })for a staff confirmation, ormanual({ input })for evidence the participant submits. - Updated dependencies [c320586]
- Updated dependencies [c320586]
- Updated dependencies [c320586]
- Updated dependencies [c320586]
- @domino-sdk/relay@1.0.0-next.2
1.0.0-next.1
Patch Changes
- e635fe2: Troubleshooting reference: when a build or typecheck fails on an API that no longer exists after an upgrade, read the Upgrade to Relay SDK v1 guide.
- @domino-sdk/relay@1.0.0-next.1
1.0.0-next.0
Major Changes
-
5d4ec3e: Start the SDK v1 prerelease. Relay SDK and CLI v1 is one breaking release, 1.0.0; until it ships, breaking changes publish as
1.0.0-next.Non the npmnextdist-tag.lateststays on the 0.x release, sonpm install @domino-sdk/relayand new starters keep using 0.x.Who should upgrade. Install
@domino-sdk/relay@nextand@domino-sdk/relay-cli@nexttogether only to try v1 before it is stable. Prereleases can break each other: pin an exact1.0.0-next.Nversion withnpm install --save-exact @domino-sdk/relay@next @domino-sdk/relay-cli@next(orpnpm add -E @domino-sdk/relay@next @domino-sdk/relay-cli@next), and upgrade the SDK and CLI to the same version.Upgrade steps. Every breaking change in the prerelease adds a section to the SDK v1 upgrade guide in the Domino docs, and its changelog entry links to that section. Once 1.0.0 is published, stable APIs follow semantic versioning; experimental APIs are only in namespaces marked experimental.
Patch Changes
- Updated dependencies [5d4ec3e]
- @domino-sdk/relay@1.0.0-next.0
0.31.1
Patch Changes
- f76abb2: Update the agent references and capability descriptions for verified identities, account-reply and kudos tracker quests, and ranking an external score with a dedicated point ledger.
- @domino-sdk/relay@0.31.1
0.31.0
Minor Changes
-
3d0bee0: Tracker triggers are composable primitives: a firing mode, an optional data filter, and the account role a quest pays, over records whose accounts can be any verified identity.
automatic({ tracker, on?, where?, beneficiary? }).onis"new-record"(default for X trackers),"new-observation"(default for custom trackers) or"data-changed"(a new record, ordatathat differs from the previous observation).whereis one condition on a path in the record'sdata:{ path, equals }, or{ path, gt?, gte?, lt?, lte? }comparing a number or an integer string exactly, with bounds that are numbers or{ setting: "name" }(an integer setting of the quest). Observations the mode or filter does not select record nothing;ingestcounts those the filter dropped inadmissions.filtered, present when nonzero.beneficiaryis an account role:"author"(default),"inReplyTo"on X trackers, or a role a custom source declares. A record never pays its author's member through another role: when the beneficiary account is the author's, or both belong to the same member (the same wallet on another chain, or another account the member linked), the observation is skipped withbeneficiary-is-author.customSource(namespace, { identities, accounts? }): record authors and named accounts are verified identities of the listed kinds,"wallet","x","discord"or"member". Records carryauthor: { id, kind? }andaccounts: { <role>: { kind?, id } };kindmay be left out when the source lists one kind. Record reads return them as ingested, and the recordsmemberfilter matches authors of every declared kind. The ingesting server names the accounts, so keep ingest keys on trusted servers.- Trigger outcomes carry a stable skip
code(trackerSkipCodes) beside the humanreason, andtrackers.outcomesfilters bycode. An X post without an author is no longer reported as "Post is not a reply". triggerMode(policy),beneficiaryRole(policy)andsourceIdentities(source)read the mode, role and identity kinds of published policies and trackers, including those stored by earlier SDKs.
Compatibility. Existing data keeps its stored shape on the wire, so SDKs and CLIs 0.25 to 0.27, and backend pods of the previous release during a rollout, keep reading it. New keys appear only where an author uses a new option:
automatic()leavesonout when it is the source's default, so default quests publish the same policy as before.on(non-default),where, roles other thanauthorandinReplyTo,identitieson new trackers,accounts,author.kind, anddiscordormemberattribution are rejected by older SDKs, whose schemas are strict: upgrade apps that read those trackers and quests.- Trackers published with
authors: "wallet"read back as stored. Republishing one asidentities: ["wallet"]is the same tracker: it keeps its key and stored definition. - Reply quests published with
beneficiary: "replied-to"keep it: reads return it as stored, and their evaluators keep receivingbeneficiary: "replied-to", so they keep working until republished. Republishing with this SDK switches them toinReplyTo. - Trigger outcomes always carry
code:trackers.outcomesneeds this SDK.
Breaking changes. For code written against the tracker triggers of 0.25.0 to 0.27.0:
- Replace
customSource(namespace, { authors: "wallet" })withcustomSource(namespace, { identities: ["wallet"] }).authorsstays readable on published trackers and is deprecated. - Replace
automatic({ tracker, beneficiary: "replied-to" })withbeneficiary: "inReplyTo". TrackerBeneficiaryandtrackerBeneficiarySchemaare removed; the beneficiary is a role string, or"replied-to"on releases published before roles.
Upgrading. Deploy the backend (migration
0074_tracker_trigger_primitives) before publishing quests that use the new options; older servers refuse them.
Patch Changes
- Updated dependencies [3d0bee0]
- @domino-sdk/relay@0.31.0
0.30.0
Minor Changes
-
c6afc57: The EVM chains
ctx.wallet.balance()reads are the platform's list, not an SDK export. Adding a chain no longer needs an SDK release.WalletBalanceQuery.chainfor EVM is any CAIP-2 ID, typed`eip155:${number}`(newEvmChainIdtype). NewisEvmChainId(value)(also from@domino-sdk/relay/authoring) andevmChainIdSchemacheck the syntax (eip155:and a chain ID in decimal, no leading zeros); the evaluator sandbox refuses anything else before reading.- The platform decides which chains it reads: the EVM networks
management.walletBalanceChains()lists, generated from dRPC's chain list. On any other EVM chain the evaluation fails with 422 "Relay cannot read balances on eip155:: it is not a supported EVM chain. …". - New
management.walletBalanceChains()(GET /management/v1/wallet-balance-chains,readpermission) returns{ chains: [{ chain, name, testnet }] }: Solana mainnet, then each EVM chain the platform reads, by chain ID. Console names the chains of a member's balance checks from it.
Breaking.
evmChains,evmChainSchema,EvmChainandEvmChainInfoare no longer exported from@domino-sdk/relay.Upgrading. Quest code that passes a string literal such as
"eip155:8453"compiles unchanged. ReplaceEvmChainwithEvmChainId,evmChainSchemawithevmChainIdSchema(it checks syntax, not support), and chain-name lookups inevmChainswithmanagement.walletBalanceChains()from your server. -
c6afc57:
ingestis a normal management permission: it sends records to custom trackers (management.trackers.ingest) and allows nothing else.- Editors hold
ingest, and organization administrators hold every permission, somanagement.access()lists it for them.rolePermissions.editorincludes it. - Personal API keys can carry it. In Console, Settings → Keys → Personal API keys now lets you choose a new key's permissions (all of yours by default) and lists each key's permissions; select only Ingest for an indexer.
POST /management/v1/tokensacceptsingestfor callers who hold it. - Hosted-app keys (
dhk_) now listingestin the minted key, the development key andaccess(), alongsidereadandpoints. They always held it. observestill allows ingestion, so keys issued with onlyobservekeep ingesting. A caller with neither gets 403 "Missing ingest permission" (before: "Missing observe permission").domino devneeds yourread,pointsandingestpermissions for its development key; editors have all three.tokenSchema(the personal API key list) drops permissions this release does not know instead of rejecting the list, asaccess()already does.
Upgrading. The
dominoCLI reads/accessand its development key without checking permission names, so every CLI version keeps working. Code that callsmanagement.access()from@domino-sdk/relay-clibefore 0.25 rejects a response that listsingest, which now happens for every editor and administrator: upgrade@domino-sdk/relay-clithere. 0.25 and later drop permissions they do not know. - Editors hold
Patch Changes
- Updated dependencies [c6afc57]
- Updated dependencies [c6afc57]
- @domino-sdk/relay@0.30.0
0.29.0
Minor Changes
-
28f8bcf: Participant profiles are primitives: Domino serves the profile and separate reads keyed by it, and your app composes the profile page. Each read checks the profile's visibility and then its own resource's access.
relay.profiles.get(id)returns the profile only:id,nameandavatar(profileSummarySchema).relay.leaderboards.placement(board, profileId)(GET /v1/leaderboards/:board/profiles/:profile) returnsrank,scoreand the rankedtotalon one board, withrankandscorenull while the member is unranked. It needs the board's access, likestandings. NewleaderboardPlacementSchema.relay.profiles.completions(id, { limit, cursor })(GET /v1/profiles/:profile/completions) returns completions grouped by quest, most recent first, paged withnextCursor. Only quests that opt in appear:defineQuest({ profile: { visible: true } }), hidden by default; the quest's active release decides, and archived quests do not appear. NewprofileCompletionPageSchema,profileCompletionQuerySchemaandquestProfileSchema; quest descriptions gain an optionalprofile, whichdefineQuestincludes only when the quest sets it.relay.profiles.referral(id, program)(GET /v1/profiles/:profile/referrals/:program) returnsinvited,qualifiedandmilestonesin one program, for signed-in viewers (401 when signed out). NewprofileReferralSchema.- React:
useLeaderboardPlacement(board, id),useProfileCompletions(id, { limit })withloadMore(), anduseProfileReferral(id, program).useProfile(id)returns the profile only. - Profile settings:
defaultsis an ordered list of the accounts that fill in names and pictures,"x"and"discord"([]for generated).profileDefaultProviderSchemalists them. - Generated avatars are seeded by an HMAC of the member under the project's secret (
relay.me.get().avatarSeed), no longer by the member's first wallet: linking a wallet never changes an avatar. proxyParticipantand the API's scope selector admit the new routes (isProfileParticipantRoute).- Console: Participant profiles settings show who sees profiles and the default accounts, with arrow buttons to order them. The member's Community profile appears only when profiles are visible or the member or an operator changed the profile.
Upgrading. Breaking for apps, servers and CLIs on 0.25 to 0.27 that use profiles:
relay.profiles.get(id)no longer returnsself,leaderboards,questsorreferrals, andpublicProfileSchemaandPublicProfileare removed. Read each part with the calls above. Forself, compareidwithrelay.profiles.mine().id.useProfile(id)'sreadystate holds aProfileSummary.- Quest completions no longer appear anywhere until the quest opts in: add
profile: { visible: true }to the quests whose completions profiles may show, and publish. - Referral progress is per program and needs a signed-in viewer.
attributedis nowinvited, returned with each program; it still counts the member's invitations across the whole project, not only that program's. management.profiles.configure()takesdefaults: ["x", "discord"]instead of"accounts", and[]instead of"generated";sectionsis removed and refused.settings()returns the list. Stored settings migrate:accountsbecomes["x", "discord"],generatedbecomes[].- Members who linked a wallet get a new generated avatar, the one members without a wallet already had. Uploaded and account pictures do not change.
- SDK and CLI 0.25 to 0.27 cannot parse the new profile and settings responses:
relay.profiles.get()andmanagement.profiles.settings()throw until the app and CLI upgrade. Deploy the backend first; its migration only adds the new setting, so pods of the previous release keep working during the rollout. - Upgrade the CLI and finish rolling out the backend before publishing a quest with
profile: { visible: true }. Only quests that setprofilecarry the key in their release. CLIs from 0.25 to 0.27 refuse deploy and preview responses that include such a release (Unrecognized key: profile), and pods of the previous backend release cannot read it, including after a rollback. Quests without it publish and roll back as before.
Patch Changes
- Updated dependencies [28f8bcf]
- @domino-sdk/relay@0.29.0
0.28.0
Minor Changes
-
30dda1d: Add
useSignInOptions({ cosmos })to@domino-sdk/relay/react: every way a participant can sign in, as one flat list. It holds the configured OAuth providers, the installed wallets of the families the project enabled, and email. Each option has anid, alabel, akindand aniconhint. OAuth and wallet options sign in withstart(), and the email option withsendCode(email)thenverifyCode(code). Cosmos wallets are offered only on the chains the app passes ascosmos.unavailableexplains the gaps with a machine-readablereason:cosmos-chains-missingwhen the app named no chains,cosmos-chains-invalidwhen a chain incosmosis invalid (such as"cosmoshub-4"withoutcosmos:; the other methods still load), andno-wallet-installedwith a message for the participant. OAuth and email are offered as soon as the methods are read; installed wallets join them when discovery finishes. The options keep their identity across renders, and their actions never reject: failures land inerror.methodsErrorsays when the methods could not be read and the list fell back to email. Apps without React get the same list fromsignInOptions(relay, { cosmos })in@domino-sdk/relay/browser, whose actions reject on failure;@domino-sdk/relay/browseralso exports the option types,discoverSignInWallets,CosmosChainsandSignInWallet.useSignIn()actions now keep their identity across renders, andverifyCodealways uses the latest code sent;sendCodeforgets the previous code first.useSignInMethods()returnserrorwhen it fell back to email. Installed wallets with the same name and family get a number in their label.cosmosChain()now rejects a chain object without a stringchainId.The starter apps now build their sign-in on the hook, and no longer export
COSMOS_CHAINS. To offer Cosmos wallets in an app made from a starter, pass your chains to theuseSignInOptions()call insrc/components/sign-in.tsx. In development the starters show a note when the project takes Cosmos wallets and the app names no chains, or an invalid one, and no longer tell participants the project has no sign-in method in that case.useSignInMethods(),useSignIn()anduseSignInWallets()keep working.Management member accounts now carry
kind:walletfor wallet identities andaccountfor the rest. Responses from older servers read asaccount.
Patch Changes
- Updated dependencies [30dda1d]
- @domino-sdk/relay@0.28.0
0.27.0
Minor Changes
-
9ef2629: Cosmos wallets work on any Cosmos SDK chain your app chooses. Relay no longer ships a list of Cosmos chains.
- A Cosmos wallet is
cosmos:<chain ID>(a CAIP-2 reference: 1 to 32 letters, digits,-or_) and a bech32 address with any prefix, checked for its checksum and stored lowercase.caip10()and wallet tracker authors accept any Cosmos chain ID. A Cosmos wallet is its address: a Cosmos account ID is attributed to the member who links that address, andtrackers.records({ member })returns the member's Cosmos records under any chain ID. The same key on chains with different prefixes is separate wallets; five Cosmos wallets per member and project, across all chains. 32-byte module and contract addresses cannot link or sign in. Unlinking a wallet and changing its sign-in accept a bech32 address in either case. - A Cosmos wallet's
networkis the chain the app requested when linking it. Relay does not verify it: any app can link an address under anycosmos:<chain ID>. Quests and servers should gate on the address, such as its bech32 prefix against their own list of chains, not onnetwork. - Browser:
cosmosSigner(provider, chain)anddiscoverCosmosWallets(chain)take the app's chain as{ chainId, chainInfo? }or"cosmos:<chain ID>". When the wallet answers that it does not know the chain, the signer suggests it withexperimentalSuggestChain(chainInfo)and retries. WithoutchainInfo, or with a wallet that has noexperimentalSuggestChain, it throws an error naming the chain. Other errors, such as a rejected request, pass through. NewCosmosChaintype andcosmosChain()helper. - React:
useWalletDiscovery({ cosmos })anduseSignInWallets(families, { cosmos })take the app's chains (one or an array). - Wallet sign-in is enabled per family:
"solana","eip155"and"cosmos".relay.auth.providers().wallets,participantSignIn()andconfigureParticipantSignIn()use families, and a per-network value such ascosmos:cosmoshub-4is accepted and read ascosmos. The rule that refused a Cosmos testnet in the live environment is gone with the chain list. NewwalletSignInFamiliesandwalletSignInNetworksSchema. - Console: Participant sign-in offers Cosmos SDK chains, and member wallets show the chain ID, such as Cosmos · cosmoshub-4. Starters offer Cosmos wallets for the chains in
COSMOS_CHAINSinsrc/components/sign-in.tsx, and say sign-in is not set up when the project enables only Cosmos wallets andCOSMOS_CHAINSis empty.
Upgrading. Deploy the backend, then the API Worker right after it. The backend's migration turns stored per-network sign-in settings into
cosmos. Until the API Worker is updated, it checksctx.wallet.list()results against the previous release's two Cosmos networks, so a quest evaluation fails when the member has a wallet linked on any other Cosmos chain.-
Projects that enabled Cosmos sign-in must upgrade their app to this release immediately after the backend deploys. SDK 0.25 and 0.26 read
relay.auth.providers().walletsagainst a fixed list of values. After the migration such a project answers["cosmos"], which that list does not contain, sorelay.auth.providers()throws for every visitor.useSignInMethods()then falls back to email alone: every OAuth and wallet sign-in button disappears, not only Cosmos wallets, and the email form appears even where email sign-in is off. -
CLI and management SDK 0.25 and 0.26 reject the
cosmosfamily inparticipantSignIn()andconfigureParticipantSignIn()responses. SDK 0.25 and 0.26 reject a linked wallet on any other Cosmos chain inrelay.wallets.list()andmanagement.member(). Upgrade the CLI with the app. -
Apps made from a starter declare
NETWORK_NAMES: Record<WalletSignInNetwork, string>insrc/components/sign-in.tsxwith a key per Cosmos network. After upgrading,tscfails on those keys. Built anyway, the sign-in form of a project that enables Cosmos wallets lists no Cosmos wallet, and crashes when it shows the "Wallet sign-in accepts …" hint, becauseNETWORK_NAMES.cosmosis undefined. Replace the per-network keys with onecosmoskey, declare your chains, and pass them touseSignInWallets:import type { CosmosChain, WalletSignInNetwork } from "@domino-sdk/relay" /** The Cosmos SDK chains this app offers wallet sign-in on. */ export const COSMOS_CHAINS: CosmosChain[] = [{ chainId: "cosmoshub-4" }] const NETWORK_NAMES: Record<WalletSignInNetwork, string> = { solana: "Solana", eip155: "EVM", cosmos: "Cosmos", } // In the sign-in component: const wallets = useSignInWallets(methods?.wallets, { cosmos: COSMOS_CHAINS, }) -
cosmosNetworksis removed, andCosmosNetworkis anycosmos:<chain ID>. Pass your chain to the adapters instead of looking it up. -
useWalletDiscovery()no longer lists Cosmos wallets on a default chain: pass{ cosmos: { chainId: "…" } }.useSignInWallets(networks)lists Cosmos wallets only with{ cosmos }. -
WalletSignInNetworkis now"solana" | "eip155" | "cosmos"; aSignInWallet'snetworkis its family, and a Cosmos wallet's chain issigner.network.
- A Cosmos wallet is
Patch Changes
- Updated dependencies [9ef2629]
- @domino-sdk/relay@0.27.0
0.26.0
Minor Changes
-
81693c8: Archive and unarchive quests published from code. Deleting a quest file never archived the published quest, and there was no way to do it.
- CLI:
domino quests archive <key> --reason "…"anddomino quests unarchive <key> --reason "…"(optional--expected-release <id>,--json). Test environment only; live changes need Console. - Management:
quests.archive(quest, { reason, expectedRelease?, actionId? })andquests.unarchive(...)callPOST /management/v1/releases/lifecyclewithpublishpermission, returning{ quest, key, title, release, lifecycle, changed }. NewquestLifecycleInputSchemaandquestLifecycleSchema. - An archived quest admits no new attempts, leaves collections and no longer counts toward
supportedInteractions; completions, attempts and rewards stay. Deploying its code again keeps it archived. Unarchiving re-checks the app's supported interactions and the prerequisite graph, and leaves the quest paused while a resource setting is empty. domino deployrelease summaries now include each release'slifecycle.
Upgrading. Deploy the backend first; older servers answer 404 for the new route. No breaking changes.
- CLI:
Patch Changes
- @domino-sdk/relay@0.26.0
0.25.1
Patch Changes
-
7420088: Hosted builds with many quests no longer fail with "Build artifact exceeds 20 MB". Quest and catalog code from
.domino-buildis now compressed and staged beside the build artifact. Its limits are 64 MB of JSON and 10 MB compressed, and the dependencies each quest bundles compress well when they repeat. The 20 MB limit now covers only Worker modules and public assets.- Contract:
buildArtifactsSchemaacceptsformat: 2artifacts, which carry adeploymentreference (stagedDeploymentSchema) and the catalog'simagesin place of inlinequestsandcatalog. Format 1 artifacts still parse.stagedDeploymentContentSchema,stagedDeploymentLimitsandstagedDeploymentKey()describe the staged object.
No changes are needed in apps. No breaking changes.
- Contract:
-
@domino-sdk/relay@0.25.1
0.25.0
Minor Changes
-
6634992: Participants have project profiles: a display name and an avatar that other participants see on leaderboards and profile pages, where the project enables them.
relay.profiles.mine(),update({ displayName, avatar, hidden }),uploadAvatar(file, { uploadId })andget(profileId). Uploads accept JPEG, PNG or WebP up to 5 MB and are stored as 512×512 WebP without metadata.- React:
useMyProfile()anduseProfile(id);avatarSrc(avatar)renders any profile avatar. Generated avatars use the same seed asrelay.me.get().avatarSeed. - Contract:
profileSummarySchema,ownProfileSchema,publicProfileSchema,profileUpdateSchema,displayNameSchema,profileSettingsSchema,profileModerationSchema,managedProfileSchemaandisProfileParticipantRoute(). Leaderboard rows gainprofile(null unless the viewer can see it; older servers omit it and it reads as null). - Management:
profiles.settings(),profiles.configure({ visibility, defaults, sections, expectedRevision, reason })(publish; in live only from Console) andprofiles.moderate(member, { resetName, removeAvatar, lock, reason })(review).member(id)returnsprofile, and account profiles gainusername. Console has Settings → Access → Participant profiles and a Community profile section on the member.
Upgrading. Nothing is shown until an operator enables profiles: leaderboards keep their aliases and profile pages answer 404. Deploy the backend first; the database migration runs automatically and dates earlier completions. Apps must upgrade the SDK so their
/relayproxy (proxyParticipantorcreateParticipantBackend) admits/v1/me/profile,/v1/me/profile/avatarand/v1/profiles/:id; older proxies answer 404 for them. X and Discord names and pictures fill in when members next sign in or connect those accounts. No breaking changes. -
531f41e: Members can link several Solana, EVM and Provenance wallets to a project and unlink them.
relay.wallets.list(),link(signer),unlink(wallet), and the lower-levelchallenge()andverify().- Dependency-free signer adapters and discovery in
@domino-sdk/relay/browser:evmSigner,solanaSigner,walletStandardSolanaSigner,cosmosSigner,discoverEvmWallets,discoverSolanaWallets,discoverCosmosWallets. - React:
useWallets()anduseWalletDiscovery(). - Evaluators:
ctx.wallet.list()returns the member's linked wallets, pinned once per attempt. - Management:
unlinkMemberWallet(member, { chain, address, reason })(requiresreview), andwalletsin the member detail. - Contract helpers:
caip10(),cosmosNetworks,isWalletParticipantRoute(), and theLinkedWallet,WalletSignerand related schemas.
Upgrading. Nothing breaks.
relay.wallet.connection()/challenge()/verify()andctx.wallet.connection()/solanaBalance()keep working and use the newest linked Solana wallet; linking throughrelay.wallet.verify()still replaces it, while wallets linked throughrelay.walletssit alongside older ones. The legacy challenge text now uses the sign-in message format, and rate-limited wallet challenges answer 429 instead of 400. Deploy the backend and API Worker before publishing apps that use the new APIs. Apps that proxy throughproxyParticipantforward/v1/me/wallets*after upgrading the SDK. The database migration runs automatically. -
c71712f: Quests can trigger from custom tracker records attributed to members' verified wallets.
customSource(namespace, { authors: "wallet" }): each record's author is a canonical CAIP-10 account ID, attributed to the member who links that wallet. Ingestion refuses missing or non-canonical authors.automatic({ tracker })admits one attempt per new record observation for the attributed member. Evaluators andkeyed()receive the pinned observation and attribution asctx.tracker;ctx.eligibleAtis the record's occurrence time.management.trackers.outcomes(tracker, query)explains each admission or skip with its reason;management.trackers.retryOutcome(tracker, input)(requiresreview) retries a skipped admission.management.trackers.records(tracker, { member })works on wallet trackers, returning the member's linked wallets' records.trackers.ingestresults includeadmissionscounts.- New
ingestpermission, granted to hosted-app keys: it allows custom tracker ingestion only. People keep ingesting withobserve.
Upgrading. Nothing breaks for existing projects. Once a project publishes a tracker-triggered quest, release and progress reads include
trigger.tracker, which older SDKs reject because the trigger schema is strict: upgrade participant apps before publishing one. Hosted-app keys holdingest, but responses (the minted key,access()) keep listing onlyreadandpoints, so older CLIs, API Workers and builders still parse them. Permission lists inaccess()projects and hosted-key responses now drop values this release does not know instead of rejecting the response. The database migration runs automatically. -
ea5d69e: Quests can check token balances of a member's linked wallets.
- Evaluators:
ctx.wallet.balance({ chain, token?, address? })reads SOL and SPL tokens (including Token-2022) on Solana mainnet, and native and ERC-20 balances on any EVM chain inevmChains(the EVM networks dRPC serves, by CAIP-2 ID such as"eip155:8453"). It reads all of the member's pinned linked wallets of that family, or one of them, and returns integer amounts with the token's decimals, captured for replay. Each read is one of the evaluation's eight capability calls. - Authoring:
parseUnits()andformatUnits()convert whole-token amounts, andWalletBalanceQueryandWalletBalancetype the call. - Contract:
evmChains,evmChainSchemaandwalletBalanceSchema. - Management: the member detail's
balanceCheckslists the member's recent recorded balance reads; Console shows them under the member's wallets. - Snippet:
token-holderchecks that a member's linked wallets hold at least a minimum.
Upgrading. Nothing breaks. Deploy the backend and API Worker before publishing quests that call
ctx.wallet.balance(). EVM chains need the platform'sDRPC_API_KEY; without it, EVM balance reads fail the verification with "EVM balance checks are not configured". Solana reads need no new configuration.evmChainsis generated from dRPC's chain list: a later release that removes a chain breaks quests that read it, and its changeset will say so. - Evaluators:
-
e7853fb: Participants can sign in, and sign up, with a Solana, EVM or Provenance wallet where a project enables it, and apps can draw generated avatars.
relay.auth.wallet.signIn(signer), and its two stepsstart(input)andverify(proof).relay.auth.providers()returnswallets, the networks the project enabled.- React:
useSignIn().withWallet(signer),useSignInMethods().wallets,useSignInWallets(networks)(the installed wallets that can sign in on those networks), anduseMe(). The starters offer installed wallets next to email and OAuth. relay.me.get()returns the signed-inmemberand anavatarSeedderived from their first wallet without revealing it.defaultAvatar(seed)anddefaultAvatarSvg(seed)draw a deterministic avatar (DiceBear Shapes, CC0);walletAvatarSeed(wallet)seeds a member's own wallet list.- Contract:
walletSignInNetworkSchema,walletSignInNetwork(),isWalletSignInRoute()andmemberSummarySchema. - Management:
participantSignIn()andconfigureParticipantSignIn({ wallets })(publish) read and set the project's wallet sign-in per environment;setMemberWalletSignIn(member, { chain, address, enabled, reason })(review) turns sign-in with a member's wallet on or off. Console has both under Settings → Access and on the member.
Upgrading. Nothing changes until a project enables wallet sign-in. Deploy the backend and API Worker first; the database migration runs automatically.
/v1/auth/providersgainswallets, which older SDKs ignore. Apps that proxy throughproxyParticipantforward/v1/auth/wallet/*and/v1/meafter upgrading the SDK. A platform without OAuth providers or email no longer answers 503 for projects that enable wallet sign-in. -
b22ead4: X trackers can trigger quests, and budgets can reset weekly.
automatic({ tracker })accepts an X tracker. The quest runs once for each post the tracker collects for the first time; refreshes, metric changes and edits never run it again.beneficiary: "author"(the default) pays the member whose selected, verified X account wrote the post;beneficiary: "replied-to"pays the member whose selected, verified X account the post replies to."replied-to"on a custom tracker throws.ctx.tracker.xcarries the captured post (contentandevidence) for X trackers, andctx.tracker.attribution.accountcan be{ provider: "x", issuer, subject, revision, linkedAt }.budget({ period: "week" })limits completions or points per ISO week in UTC, starting Monday, by the attempt's eligibility time.- New trigger outcome reasons: "Post is not a reply", "Reply is to the author's own post", "No member has linked this X account" and "Captured post is unavailable".
Upgrading. Nothing breaks for existing projects. Release and progress reads that include an X tracker trigger (
source: "x-search",beneficiary: "replied-to") or aperiod: "week"budget are rejected by older SDKs, whose schemas are strict: upgrade participant apps before publishing either.
Patch Changes
- 0b5ee21:
domino devruns against a Domino API that reports direct preview transport, such as Domino's local development stack, without cloudflared: the preview routes straight to the app's port, so the CLI starts no connector and skips the public reachability check. On such a stack without Workers AI credentials,domino devkeeps running on the quests already published instead of stopping.developmentPreflightSchemagainsdevelopment.transport("tunnel"or"direct"), defaulting to"tunnel"for APIs that do not report it, so existing behavior is unchanged. - Updated dependencies [6634992]
- Updated dependencies [531f41e]
- Updated dependencies [c71712f]
- Updated dependencies [ea5d69e]
- Updated dependencies [e7853fb]
- Updated dependencies [b22ead4]
- @domino-sdk/relay@0.25.0
0.24.0
Minor Changes
-
305ca73: Hosted apps can now have secrets. Add
domino secrets set NAME,domino secrets list, anddomino secrets delete NAMEto manage them in the project's test environment.setreads the value from stdin or a hidden terminal prompt, never from arguments, and never prints it. Worker builds (app.kind: "worker") staged after a change receive every secret as a secret binding, replacing an appvarsentry of the same name; rundomino stageagain to apply a change. Static apps anddomino devreceive none.- The management client adds
secrets.list(),secrets.set(name, value), andsecrets.delete(name), forGET /management/v1/secrets,PUT /management/v1/secrets/:namewith{ value }, andDELETE /management/v1/secrets/:name. Values are write-only: no route returns them. - Worker apps declare the secrets they read in
relay.jsonapp.secrets:{ "APP_API_KEY": { "description": "…", "required": true } }.requireddefaults to true. A name cannot be both avarsentry and a declared secret, and static apps cannot declare any.domino stagewarns on stderr about each required declared secret that is not set, with the command that sets it, and addsmissingSecretsto its result; the build still stages.domino secrets listreturns the set secrets, the declared ones (the active build's merged with this checkout'srelay.json), andmissing.GET /management/v1/secretsincludes the active build'sdeclaredsecrets. - New exports:
appSecretNameSchema,appSecretValueSchema,appSecretInputSchema,appSecretSchema,appSecretListSchema,appSecretDeclarationSchema,appSecretDeclarationsSchema, theAppSecretDeclarationtype, andAPP_SECRET_LIMIT. - Names use uppercase letters, digits, and underscores (at most 64 characters, not starting with a digit);
DOMINO_names andASSETSare reserved. Values are 1 byte to 5 KiB, and a project holds at most 50. Setting and deleting needpublish, listing needsread. Other environments answer 422.
Upgrade notes:
- Additive. Existing commands, client methods, and
varskeep working. Older CLIs reject arelay.jsonthat declaresapp.secrets; upgrade the CLI before adding declarations. - The commands need a server with this release; older servers answer 404. A server without app secret storage configured answers 503.
- Move credentials you placed in
relay.jsonapp.varsinto secrets and remove them from the repository. The API starter now declaresAPP_API_KEY; set it this way (domino secrets set APP_API_KEY, thendomino stage) to open its protected routes on the staged preview. - Refresh installed agent guidance with
domino agents installafter upgrading.
- The management client adds
Patch Changes
- @domino-sdk/relay@0.24.0
0.23.1
Patch Changes
- e4d0a4f: Fix
domino devmissing rapid, same-size source edits on filesystems that report identical modification timestamps. Source polling now compares file contents as well as metadata. No configuration or migration is required. - @domino-sdk/relay@0.23.1
0.23.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
-
49fa0fd: Apps can now match the ledger and tiered reward IDs that reads return to the keys they declared.
relay.pointLedgers.list()(GET /v1/point-ledgers) and the React hookusePointLedgers()return each ledger'sid,key, andname.relay.tieredRewards.list()(GET /v1/tiered-rewards) anduseTieredRewards()return each tiered reward'sidandkey.- Both reads are open to signed-out visitors. The hooks return the list in
items.keyis null for resources created in Console without one.
Reads name ledgers and tiered rewards by ID. Comparing a referral reward's, account's, or history entry's
balancewith a key such as"credits", or an entitlement'sreward.idwith the key given todefineTieredReward, never matched, and apps could show "200 ledger_… points". Find the resource by key, compare IDs, and display its name or title.Quest view rewards are now typed:
{ kind: "points", label, amount, ledger: { id, key, name } },{ kind: "tier", label, reward: { id, key, title }, tier }, or{ kind: "integration", label }. Labels read "10 XP" instead of "10 ledger_…". Tier labels show the reward title, followed by the tier title when the reward has several tiers, instead of the tier ID.Upgrade notes:
- Code that reads
labelkeeps working, and older SDKs ignore the new fields. Replace label parsing withamount. If you compared a tier reward'slabelwith its tier ID, comparetierinstead. - TypeScript code that builds quest views, such as test fixtures, must add
kindand that kind's fields. - Deploy the updated backend and API Worker before upgrading the SDK: this SDK expects
kindon quest view rewards. Apps that proxy participant requests withproxyParticipantorhandleHostedAppforward the new routes once they upgrade. - No database migration is required.
Patch Changes
- Updated dependencies [49fa0fd]
- @domino-sdk/relay@0.23.0
0.22.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
-
024b075: Add
domino feedbackfor humans and agents to report tooling issues to Domino's Linear Triage queue. Submit text directly, from a file, or from stdin; use--dry-runto review the report and--jsonfor automation. The bundled agent skill now instructs agents to report tooling problems and include the issue link in their handoff.At task completion, agents also review and report difficulties that better SDK or CLI support could address, including manual work and workarounds during otherwise successful tasks. They skip duplicates and reports with no tooling difficulty to describe.
Requires Domino authentication and a backend with feedback delivery configured by the operator. Existing commands remain compatible. Refresh installed agent guidance with
domino agents installafter upgrading. -
2614ad4: Add
ctx.imagesfor quest evaluators:inspect,compare(perceptual hash distances),review(AI judgement over up to four images),extract(typed fields read from a screenshot) andseen(other participants' near-duplicate images). Images are named by reference: the attempt's photo evidence, the linked X profile picture, an X post's attached image, or a reference image listed under the newimageskey inrelay.json(JPEG, PNG or WebP, up to 5 MiB each and 10 per deployment) and published withdomino deploy, which uploads new files directly to storage and sends only their hashes. New snippetsx-profile-picture,unique-photoandcommunity-screenshotinstall complete quests with operator-tunable similarity thresholds.ctx.x.post()results now include attachedmedia.Additive and backward compatible. Existing quests, including
ctx.review.photo(), keep working. To usectx.images, rebuild quests with this SDK version; bundles built with earlier versions do not pass it toevaluate. Deployingimagesneeds a server with this release; older servers ignore the key, and quests that reference those images then fail verification with "Image … is not published".
Patch Changes
- Updated dependencies [2614ad4]
- @domino-sdk/relay@0.22.0
0.21.0
Not published to npm. Its changes first reached npm in 0.23.1.
Minor Changes
- b605061: Let organization admins delete a project or move it to another organization. Add
deleteProject(DELETE /management/v1/project) andtransferProject(POST /management/v1/project/transfer) to the management client, with theprojectDeletion*andprojectTransfer*schemas. Both need the project's name asconfirm. A deleted project stops answering at once. Domino support can restore it for 30 days, after which its data, hosted app, repository and evidence are erased. Only a project without participants can move, and its hosted app keeps working without a redeploy: a hosted app key now decides its organization, so a staleX-Relay-Organizationfrom the app is ignored rather than refused. When a project moved, management routes tell members of its new organization where it went (PROJECT_MOVED), and the CLI offers to follow it: it signs in to that organization if needed, updates the checkout and retries.domino login --organization <id>does the same without a prompt.
Patch Changes
- @domino-sdk/relay@0.21.0
0.20.0
Not published to npm. Its changes first reached npm in 0.23.1.
Patch Changes
- Updated dependencies [2ff82fb]
- @domino-sdk/relay@0.20.0
0.19.0
Minor Changes
- cea3ca7: Require sign-in on every staging app. Visitors sign in with Domino Console and need read access to the project in Test, or enter a password that a project owner sets under Settings → Staging access. Console's Open app links sign in directly. Add the
stagingAccess*andstagingLinkschemas forGET/PUT /management/v1/staging/accessandPOST /management/v1/staging/link. This is a breaking change for scripts that fetch staging URLs without a session. They should mint a link withPOST /management/v1/staging/linkand send the cookie it sets. Staging now reserves the/__domino/staging-accesspath, including for transparent routing.
Patch Changes
- c2041e7: Fix playground and
domino devpreviews crashing withENOSPC: System limit for number of file watchers reached. Quest synchronization now polls source files about once a second and skips dependency, build, and runtime directories before scanning. App hot reload still uses the app's development server. No configuration changes are required; upgrade the CLI and restart affected previews. - @domino-sdk/relay@0.19.0
0.18.4
Patch Changes
-
68a0429: Use published npm packages for new starters instead of bundled SDK and CLI archives. Exports pin both packages to the same release and include a lockfile. Starter provenance now lives in
.domino/starter.json, separate from legacy archive metadata.Doctor reports installed SDK and CLI versions and no longer requires obsolete starter archives. To migrate an existing project, replace Domino's
file:.domino/*.tgzdependencies with the same exact published npm version, remove theirpnpm.overridesentries, install, and run the app checks. Then delete the unused package archives and old.domino/release.jsonarchive manifest. Preserve unrelated dependencies and overrides.The Playground offers Upgrade with AI for CLIs older than 0.18.3 or projects with local package pins before launching the preview. Dependencies change only when the owner selects the action.
-
@domino-sdk/relay@0.18.4
0.18.3
Patch Changes
- da8c475: Fix previews that fail after Vite reports ready. Local and public startup checks now request HTML, so apps with a browser-only page fallback, including the Classic starter, can start successfully. Upgrade the project's CLI and restart the preview; no app configuration changes are required.
- @domino-sdk/relay@0.18.3
0.18.2
Patch Changes
-
0b04a5f: Install small Codex and Claude skill loaders that read guidance from the project-local CLI package. New starters point to the same bundled skill from AGENTS.md, and CLAUDE.md points to AGENTS.md. Guidance now follows dependency upgrades without routine skill reinstallation. Agents must re-read the skill after upgrading, and dependencies must be installed before the loader target is available.
Existing checkouts should upgrade their project-local CLI, run
domino agents installonce, and commit the instruction changes. The installer migrates known unmodified copies, keeps package-backed redirects for old reference links, and adds marked pointers to AGENTS.md and CLAUDE.md while preserving project instructions. It reports customized files before writing; move their project-specific rules to AGENTS.md, reconcile the reported files with the bundled skill, and retry.domino doctordistinguishes missing package guidance from copied or customized instructions. Installation results now include anupdatedfile list, and new installations create loaders instead of copying all references.The bundled hosted workflow teaches agents to start from current
main, preserve and reconcile resumed work, and integrate changes intomainbefore shared staging. The CLI and backend do not yet enforce that branch policy or merge work automatically. -
@domino-sdk/relay@0.18.2
0.18.1
Patch Changes
-
2186fe3:
domino devno longer breaks participant requests when--api-urlis Console's management proxy (https://console.domino.run/relay). The CLI used to pass that URL to the app asDOMINO_PARTICIPANT_API. The participant proxy refuses any upstream with a path, so every/relay/*request answered 503 "Participant API is not configured".The API now reports its participant origin in
/preflight(participantApi).domino devsends participant requests there and gives the development management key the same origin, as deployed builds receive it. With an older API, the CLI uses--api-urlif it is already an origin, then relay.json'sapiUrl. If neither is, it stops with instructions instead of starting a broken preview.domino doctor --remotehas a newparticipant-apicheck, and reports the resolved origin asscope.participantApi. The check fails when no participant origin can be found, or when relay.jsonapiUrlis not that origin, which staged builds rely on. The proxy's 503 message now says to use the API origin rather than a management URL.Upgrade note: if relay.json
apiUrlis set to a management proxy URL, change it to the participant API origin (https://relay.domino.run). -
Updated dependencies [2186fe3]
- @domino-sdk/relay@0.18.1
0.18.0
Minor Changes
-
bf84375: Add
trackers.content(reference, { id, observation })to hosted quest evaluators. The read returns a retained X body and verified account attribution to the attempt's member, captured together for replay. Engagement counters, mutable author profiles, usernames, and verification badges do not determine ownership. Explicit disconnects and ambiguous legacy connections return unlinked attribution.Management clients expose the same read with a required
memberin the query andreadpermission. Deploy the updated backend and API runtime before using this method. Existing tracker methods and response shapes remain unchanged. This adds evidence reads, not automatic admission or engagement rewards. -
ac63da2: Add automatic X content quests with
content(tracker, policy)andperContent(), pinned content context, quest enrollment, historical claims and staff approvals, and paginated admission explanations. Existing trigger and execution contracts remain readable. Upgrade the backend and collector workers before publishing content quests; update participant apps to use the new SDK proxy paths. Existing tracker matches are not backfilled, and edits after admission do not reevaluate or reverse prior rewards. -
0a0ac9a: Add independent daily engagement rewards for qualified X content. Author immutable cumulative formulas with
defineEngagementReward, publish bundled code withmanagement.engagement.publish, and read settlement history throughengagement.page. Partial budgets retain paid and permanently excluded growth separately.Deploy backend migration 0060, the API formula runtime, and workers before publishing engagement rules. Existing quests keep their contracts. This first version supports new discoveries and historical initial baselines; CLI manifest deployment, formula replacement, explicit backfill, enrollment baselines, and automated reversals remain unsupported.
-
40c9761: Add opt-in X evidence references to tracker records. Pass
includeEvidence: trueto receive a stable content revision and a separate provider observation identity. Engagement and author profile refreshes keep the content revision unchanged. Custom records returnevidence: nullwhen requested; default responses keep their existing shape.Deploy backend migration 0056 and the updated API before requesting evidence from the new SDK. The migration backfills retained X observations and captures writes from previous backend versions during rolling deployment. Existing SDK callers need no changes. Automatic content qualification and engagement rewards remain unimplemented.
-
b596a0a: Add content-only performance gates with declared engagement metrics and a configurable freshness limit. Content approval stays cached while new samples run performance. A body edit or change to the verified account selection requires fresh content approval. Missing or stale metrics wait without running the callback. The first passing performance decision resolves the content-approved reward selection; later metric changes cannot duplicate awards or retry budget exclusions.
Use
content.performance({ quest, content, member?, before? })to read current eligibility and retained metric evaluations. Existing content qualification history remains content-only. Performance callbacks receive the declared metrics, sample time, pinned content, member, and settings. They gate rewards but cannot replace the content-approved selection.Apply migration
0059_content_performanceand upgrade the backend, API evaluator, and workers before publishing quests with this stage. Older evaluator runtimes cannot dispatch performance safely. Existing quests need no source changes and omit the new optional fields. Upgrade the SDK to author performance stages or read their history. Automatic reversals and daily engagement settlement remain unsupported. -
e5b7091: Reevaluate admitted X posts after body edits and expose
content.qualifications({ quest, member?, content?, before? })in participant and management clients. History keeps the original release, beneficiary, and evidence, with qualification state separate from the one-time reward outcome. Stale evaluations and reviews cannot award rewards; later qualifying edits cannot repeat rewards or retry a budget exclusion. Existing admission APIs retain their response shapes. Removal and rejection do not reverse prior awards.Apply migration
0058_content_qualificationand upgrade backend and workers together before enabling content quests. Older decision workers cannot enforce stale-content guards during mixed-version deployment. Existing admissions are retained and backfilled; no quest source changes are needed. Upgrade clients to use the new history method. -
243d9e7: Add explicit
engagement: [{ entry: "engagement.ts" }]manifest entries, with optionalexportName, todomino buildanddomino deploy. Preview and publish engagement rules with their tracker, qualifying quest, and points balance in one project transaction. Previews report new, unchanged, and changed formulas; formula changes preserve past earnings and budget spend through observed boundaries.Resume the saved deployment ID to publish a reviewed preview or recover a committed result. Stale engagement changes reject the entire deployment. Missing entries do not delete rules, and unchanged code does not create another scoring version. New rules use actual publication time as their discovery cutoff.
Upgrade the backend and settlement workers before deploying engagement entries. Manifests and SDK requests without engagement entries keep their request shape, and existing hosted-build activation receipts remain replayable. CLI publication remains test-only; live previews remain available. Changes to metrics, budgets, or qualifying quests remain unsupported.
-
4cea332: Add preview/apply management workflows for engagement formula changes, immutable version history, and participant boundary history. Each post settles its governing formula through an available fresh eligible observation before the latest requested formula starts at the same counters. Existing budget spend and exclusions survive; superseded unapplied formulas do not invent intermediate earnings.
Preview totals describe retained accounting, not promised future payouts. Available observations may predate the request by up to the rule's freshness limit. Quest, balance, metrics, baseline policy, freshness policy, and budgets cannot change in this workflow.
Deploy migration 0061 and upgrade/drain all settlement workers before requesting a formula change. Older workers do not enforce version boundaries. Existing settlement response shapes remain compatible; histories may now include an additional formula-boundary settlement in the same UTC day.
Engagement creation and formula apply now follow the existing live-publication policy: management keys can publish in test, while live publication requires a Console session.
-
2a7ca88: Add an optional
contentfilter tocontent.admissions({ quest, content })for direct post lookup. Management and participant reads retain their existing project and member isolation, response shapes, and unfiltered pagination behavior. Upgrade the backend before sending the new filter; older servers reject it. Existing callers need no changes.
Patch Changes
- Updated dependencies [bf84375]
- Updated dependencies [ac63da2]
- Updated dependencies [0a0ac9a]
- Updated dependencies [40c9761]
- Updated dependencies [b596a0a]
- Updated dependencies [e5b7091]
- Updated dependencies [243d9e7]
- Updated dependencies [4cea332]
- Updated dependencies [2a7ca88]
- @domino-sdk/relay@0.18.0
0.17.2
Patch Changes
-
664eb80: Share current X post content, author verification, and engagement across matching trackers in the same project and environment. Each tracker keeps its own matches and first-seen times. Concurrent collectors coordinate known-post refreshes, and captured quest reads retain their original results.
Pausing a tracker stops its collection; another active tracker can still update their shared posts. Custom datasets remain isolated per tracker. Existing tracker definitions and SDK calls need no changes.
Requires backend migration
0055_shared_tracker_contentand the matching backend deployment. The migration retains the previous release's write format for rolling upgrades and preserves existing observation history. Removing the legacy payload columns requires a later contract release after all old workers have stopped. Old workers may still perform duplicate refreshes until the rollout finishes. -
Updated dependencies [664eb80]
- @domino-sdk/relay@0.17.2
0.17.1
Patch Changes
- ff2c1ee: Remove the packaged project-maps agent reference. Console now draws the interaction map, user flow, and decision matrix from published project state under Project map.
domino agents installno longer addsreferences/project-maps.md; an installed copy can be deleted. - @domino-sdk/relay@0.17.1
0.17.0
Minor Changes
- 052417c: Add packaged agent guidance for creating evidence-backed interaction maps, user flows, decision matrices, and board-ready project views.
Patch Changes
- @domino-sdk/relay@0.17.0
0.16.1
Patch Changes
- aedef4f: Fix classic, waitlist, and referral starter compatibility with text and URL submission types. Quiz recovery now reads only quiz attempts, and classic interaction labels cover the expanded SDK union. Supported interactions remain unchanged.
- @domino-sdk/relay@0.16.1
0.16.0
Not published to npm. Its changes first reached npm in 0.16.1.
Minor Changes
-
3280aa1: Add required or optional text and HTTP/HTTPS URL submissions with configurable text lengths and exact allowed hosts. Evaluators receive captured strings through ctx.input.value. Browser handles and campaign controls preserve the original values across interrupted delivery and evaluation retries. Backend admission enforces the published contract and returns correctable validation errors.
Submitted URLs never trigger automatic fetching. Use
xPostIdandctx.x.post({ postId })to verify a post through the configured X provider; reads are captured for retries. Participants link X before verification. Compare the returned author ID withuserIdfromctx.x.connection()to establish authorship.Existing photo and quiz request shapes and saved photo records remain compatible. Upgrade the SDK and CLI together before authoring text or URL quests. Existing participant apps must add controls and declare text and url in supportedInteractions; newly exported campaign starters include both. Deploy the updated backend and API Worker before publishing these quests. Text and URL storage need no additional migration; the integrated X capability requires the X execution-snapshot migration.
-
074c292: Add captured X verification to authored quest evaluators:
x.connection(),x.profile(),x.post({ postId }),x.follows({ userId }), andx.reposted({ postId }).Participants link their X accounts through the existing account flow. Platform operators configure the backend's
X_API_WRAPPER_API_KEY, shared with trackers. Account changes invalidate unfinished reads; retries reuse captured evidence. Existing quests remain compatible. Apply the additive X execution-snapshot migration and deploy the updated backend and evaluator Worker before publishing quests that use the new methods, and update the SDK and installed CLI references.
Patch Changes
-
abc6358: Add
ctx.review.text({ text, criteria })with typed pass, fail, and unclear results for authored quest decisions. Review verified content such as X tracker posts using the existing backend AI configuration. Validated results are captured and reused across retries; provider failures remain errors.The capability limits text to 16,000 JavaScript string units and criteria to 2,000, with a 12-second provider timeout. Review text is treated as untrusted evidence. Credentials stay server-side. This additive API requires a backend and evaluator bridge deployment that supports text review before publishing quests that use it. Existing photo quests remain compatible. Combine this capability with
evidence.text()for submitted text orevidence.url()plus verified X post reads. -
Updated dependencies [abc6358]
-
Updated dependencies [3280aa1]
-
Updated dependencies [074c292]
- @domino-sdk/relay@0.16.0
0.15.0
Minor Changes
-
6c309ea: Hosted apps can call the management API from their own server code without a pasted credential. Every staged Worker build now receives a platform-managed key as the secret binding
DOMINO_MANAGEMENT_KEY, plusDOMINO_API_URL,DOMINO_ORGANIZATION,DOMINO_PROJECT, andDOMINO_ENVIRONMENT. Build a client withhostedManagementClient(env)from@domino-sdk/relay-cli, which is safe to bundle into a Worker withoutnodejs_compat. The key (dhk_…) has exactlyreadandpointsin the project's test environment, acts ashosted:<repository ID>rather than a person, and is revoked when a newer build activates or its own build fails.domino devnow requests a 12-hour development key (it needs your ownreadandpointspermissions in the test environment), exports the same five variables to the dev server process, and revokes the key when it stops. An app'svite.config.tsmust forward them to the local Worker in thecloudflare()plugin's developmentvars; apps created from earlier starters need that addition.Upgrade notes: the five
DOMINO_names are now reserved; appvarswith those names are replaced by Domino's values. Deploy the backend (migration0053_hosted_app_keys) and API Worker before the builder, because the builder fails a Worker deployment when it cannot obtain a key. Older CLIs keep working and simply do not export a development key; a new CLI against an older API prints that no key is available and continues. -
6c309ea: New projects can start from a choice of starters: Campaign tour (the default), Classic questboard, Waitlist, Referral program, Discord activation, and API. In Console, choose one under Start from when creating something new. In a terminal, run
domino create NAME --template waitlist, or answer the new prompt. Scripts without--templatestill get the campaign tour.starterTemplatesandstarterIdSchemain@domino-sdk/relay-clilist the offered starters. Projects now report the chosen starter as a top-levelstarterfield, andPOST /repositoryaccepts any offered starter ID. Omit it to use the starter chosen at setup. At creation, Domino writes the project name to the starter'ssrc/project.jsonand places the brief where the starter'sCAMPAIGN.mdmarks it.@domino-sdk/relay/reactprovides the React layer the starters share:RelayProvider,useExperience,questGroups,useQuest, and the quest status helpersuseSignIn,useSignInMethods,useSignOut, anduseAccountsuseLeaderboard,useReferralSummary,useInvitation, anduseRelay
React 19 is an optional peer dependency.
@domino-sdk/relay/viteaddsserverBoundary(), which fails a Vite build when browser code importsserver/or@domino-sdk/relay-cli.@domino-sdk/relay/portal-proxyaddshostedAppRequestandhandleHostedAppfor the Domino routes of a hosted app's Worker.Upgrade notes: nothing breaks for existing projects. Apps built from the earlier campaign starter keep their own
src/relaycopies and can switch to@domino-sdk/relay/reacthook by hook. Wrap the app inRelayProvider, which replacesExperienceProviderandsrc/relay/client.ts, then change imports. The hook names and behavior are unchanged, except thatuseRoleDeliveriesis now internal anduseExperience()also returnsmember.
Patch Changes
- Updated dependencies [6c309ea]
- @domino-sdk/relay@0.15.0
0.14.1
Patch Changes
-
9f6c8db: Remove CLI and agent guidance for the retired Console Releases page and JSON imports. Use
domino deploy --environment testfor code-defined behavior anddomino stagefor hosted test apps. Quest Studio publication remains available for Console-authored quests.Code-defined live publication through Console is no longer available. Existing live definitions keep running. The CLI continues to reject live publication;
domino deploy --preview --environment livestill validates without publishing. Offline bundle exports remain supported. -
@domino-sdk/relay@0.14.1
0.14.0
Minor Changes
-
11abaf5: Add a bounded X search preview for exploring tracker queries before defining them in code.
management.trackers.preview({ query, since })returns at most 100 matching posts from 3 search pages, up to 30 days back, with author verification retained andmoreset when X had further matches. It requirespublishpermission, is rate limited, stores nothing, and never publishes a tracker or starts collection. Console's Trackers page uses it to show a sample and copy a tracker definition or coding-agent prompt.Requires the matching backend deployment. Existing clients and manifests are unaffected.
Patch Changes
- Updated dependencies [11abaf5]
- @domino-sdk/relay@0.14.0
0.13.0
Minor Changes
-
e969f1e: Add maintained tracker datasets with a managed X search source, preserved author verification, read-time filters, and custom indexer ingestion. Publish definitions through relay.json and the existing deployment preview flow. Query datasets from the management SDK or captured read-only capabilities in hosted quest evaluators.
Requires the matching backend migration and deployment before publishing or reading trackers. X collection requires the platform's X_API_WRAPPER_API_KEY credential. Existing project manifests remain valid. Custom evaluator test contexts that explicitly implement EvaluationContext must add a trackers reader. Collection does not automatically issue or reverse rewards.
Patch Changes
- Updated dependencies [e969f1e]
- @domino-sdk/relay@0.13.0
0.12.0
Minor Changes
- 9fdc113: Reward deliveries now end.
rewardDeliverySchema.statusadds a terminalfailedstate, reached when Discord permanently rejects a role grant (unknown role, member left the server, missing permissions) or transient errors persist for about a day. The management client addsretryRewardDelivery(id), which moves a failed delivery back topendingand starts a new delivery round. Clients that parse deliveries with an older SDK rejectfailedrecords, so upgrade before reading deliveries.
Patch Changes
- Updated dependencies [9fdc113]
- @domino-sdk/relay@0.12.0
0.11.0
Minor Changes
-
5337971: Add
streak({ days, timeZone })for automatic observation quests. Verified actions count once per community day, including daylight-saving boundaries. Late evidence repairs continuity; milestone rewards commit once with progress. Participant reads expose current/best runs, qualifying days, deadlines and reward status. Timezone changes take effect at the next community boundary without rewriting history.Each quest awards one lifetime milestone. Configure separate quests for separate goals. All streak quests in a project/environment share one timezone. An existing goal length is immutable, and budget-exhausted milestones keep their evidence without later automatic payout.
Apply the backend migration and deploy the updated backend, SDK and participant app before publishing streak quests. Existing once/keyed quests retain their behavior. Older SDKs cannot parse the new
streakcompletion kind orprogressedattempt status; upgrade clients that read a project containing streak quests.
Patch Changes
- Updated dependencies [5337971]
- @domino-sdk/relay@0.11.0
0.10.0
Patch Changes
- Updated dependencies [8a81fde]
- Updated dependencies [d5d530d]
- @domino-sdk/relay@0.10.0
0.9.1
Patch Changes
- a024053: Link doctor output and bundled agent guidance to the public package changelog and upgrade instructions. When SDK and server behavior disagree, agents can review intervening release notes, upgrade packages together, and retry the failed action. Guidance is advisory and does not automatically update dependencies or claim server compatibility.
- @domino-sdk/relay@0.9.1
0.9.0
Minor Changes
-
6a5b6d3: Remove the offline development runtime and the
dev --offline,dev --review-mode, anddoctor --offlineoptions. Usedomino devwith the hosted test environment and real sign-in. New campaign exports include only the SDK and CLI packages.New release and catalog publication accept only Workers AI. Historical provider identifiers remain readable for retained records, but cannot select simulated evaluation.
Patch Changes
- Updated dependencies [6a5b6d3]
- @domino-sdk/relay@0.9.0
0.8.0
Minor Changes
- 25ea110: Assign persistent resource IDs on the server. Authoring declarations now use stable keys, project and draft creation use separate retry keys, and point ledgers must be created or declared before use. Existing IDs remain valid. Legacy authoring
idremains supported as a key alias. Usedomino migrateto preview a repeatable source and manifest upgrade, apply with--write, and inspect actionable diagnostics withdomino check --json. Creation helpers generate retry keys, retry uncertain responses once, and expose the retry key onCreationError; raw HTTP creation still requires an action ID. Consume returned IDs for newly created resources.
Patch Changes
- Updated dependencies [25ea110]
- @domino-sdk/relay@0.8.0
0.7.0
Minor Changes
- 0bbefc1: Add point ledger metadata with stable IDs, editable names and descriptions, management list/save methods, and a pointLedger setting for Console pickers. Existing balance keys retain their accounting history.
Patch Changes
- Updated dependencies [0bbefc1]
- @domino-sdk/relay@0.7.0
0.6.0
Minor Changes
- 5cd39a2: Add observation-triggered quests, keyed completions, visibility, shared atomic budgets, and backend observation submission. Include an automatic Discord announcement reaction snippet and participant guidance for repeatable quests.
- 5cd39a2: Add Discord message contributions, moderator recognition with separate actor and beneficiary identities, richer member and scoped message reads, and durable Discord role rewards with reusable role selectors and delivery status. Include three distinct capability snippets and updated authoring guidance.
- d7dd5a4: Show readable command results by default and offer interactive project selection for checkout, init, and inspection commands. Prompt for missing create inputs, agent selection, and recent build selection. Confirm deployment and staging in terminals, with --yes to skip confirmation. Use --json for structured results and --no-interactive to disable terminal prompts.
- 7c88acd: Add
peopleto management list reads and apeople(ids)client method. Both return the sign-in accounts and profiles of members that records name only by ID, so tools can show who a member is outside the Members list. - 5cd39a2: Add reusable integration resource selectors and date/time settings. Include Discord channel selection, scoped resource lookup, publication validation, and paused publication for unconfigured resource settings.
Patch Changes
- 7216980: Report only staging blockers when staging preflight fails. Do not include optional participant sign-in diagnostics in the error.
- Updated dependencies [5cd39a2]
- Updated dependencies [5cd39a2]
- Updated dependencies [5cd39a2]
- @domino-sdk/relay@0.6.0
0.5.0
Minor Changes
-
85bb8aa: Add compact prefixed resource ID generation and expose globally unique project slugs. Hosted builds carry the slug used for stable staging addresses; development previews use the slug and a shorter workspace hash. Existing resource IDs remain valid.
-
990c756: Support compiled Cloudflare Worker apps in connected previews and cloud staging, including SSR, server actions, and API routes. Add the worker app configuration and private module artifacts while preserving static hosting.
Add opt-in
app.routing="transparent": app requests and responses pass through unchanged, with no reserved routes. Move the starter participant proxy into application code and add an explicit hosted participant integration option. Omitted routing retains legacy behavior for existing projects, deployed builds, preview sessions, and older CLI requests. New starters opt in; existing apps migrate explicitly after adding their own participant handler.
Patch Changes
- 8b454af: Show configuration sources and pinned install commands in setup diagnostics, label the bundled capability catalog with release provenance, and preserve Git checkout failure details. Clarify recovery and verification in the shipped agent guidance. Create missing parent directories for deployment build output.
- Updated dependencies [85bb8aa]
- Updated dependencies [c24042e]
- Updated dependencies [990c756]
- @domino-sdk/relay@0.5.0
0.4.0
Minor Changes
- d8a2572: Add project integrations with managed Discord bot installation in Console. Quests declare integrations and read membership through ctx.discord.member() without a server ID. Participants use basic Discord identity sign-in or linking. Capture identity and integration revisions for recovery, provide starter identity controls and failed-submission discard, and include the membership snippet and operator/agent guides.
- 8ead9ee: Add project-scoped points leaderboards with code-defined calculations, reviewed publication, participant rankings, and audited points updates. Support period windows, shared ranks, participation groups, and board exclusions.
- 7204e04: Discover default-exported quests and quest types using patterns in relay.json. New projects include discovery patterns, and explicit entries can override deployment settings. Reject missing selected exports while bundling. Add editable photo, quiz, and staff-confirmation recipes through the public snippets registry.
- 1fb2edd: Add code-defined referral programs, session-bound invitation attribution, qualification rewards, milestones, and selective inviter point bonuses with correction recovery. Include participant and management APIs, project publication, Console controls, and agent references.
Patch Changes
- d8a2572: Guide agents to discover recipes from the live snippet catalog before authoring quests. Expose the workflow through
domino reference authoringand link it from generated project guidance. - 44e1436: Guide agents to research branding and campaign context, propose a visual direction and campaign brief, and wait for owner confirmation before the first implementation. Preserve confirmed decisions for later sessions.
- 22e5523: Describe the rebuilt starter in the hosted-project agent reference: a static TanStack Router and Tailwind CSS v4 app with a neutral look, sample tour quests and
src/relayhooks for session, quest state, submission, and recovery. - Updated dependencies [d8a2572]
- Updated dependencies [c475d40]
- Updated dependencies [8ead9ee]
- Updated dependencies [7204e04]
- Updated dependencies [1fb2edd]
- @domino-sdk/relay@0.4.0
0.3.0
Minor Changes
- 53d6aef: Support existing application logins through a server participant adapter and project-scoped identity integration keys. Add Console provisioning, rotation and revocation, trusted participant routing, and an agent integration reference.
Patch Changes
- Updated dependencies [53d6aef]
- @domino-sdk/relay@0.3.0
0.2.0
Minor Changes
- 9c037ee: Use hosted test data and leased HTTPS previews for development, with an explicit offline runtime option. Fix unified deployment startup, preflight staging prerequisites, and bundle capability discovery, participant API, referral and troubleshooting references. Record exact starter package hashes and verify the distributed development journey.
- 8aba51d: Save the project setup mode and brief, and include the brief in newly provisioned starter repositories. Console guides new and existing projects through agent handoff with resumable setup.
Patch Changes
- @domino-sdk/relay@0.2.0
0.1.0
Minor Changes
- d46d47c: Introduce the Relay SDK, Domino CLI with bundled agent skills, and standalone local runtime.
Patch Changes
- Updated dependencies [d46d47c]
- @domino-sdk/relay@0.1.0