@domino-sdk/relay 1.61.0 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +156 -8
  2. package/README.md +3 -2
  3. package/dist/{schema-CZ6Rj0ZF.d.ts → accounts-CCqCKCVx.d.ts} +3127 -1764
  4. package/dist/action-9Xl0StBg.d.ts +33 -0
  5. package/dist/{integrations-B3y36OaB.d.ts → analytics-BwAKLi7O.d.ts} +347 -57
  6. package/dist/{attempt-captures-DYnJVkfg.d.ts → attempt-captures-Cm-5G0PC.d.ts} +1 -1
  7. package/dist/authoring.d.ts +130 -42
  8. package/dist/authoring.js +112 -37
  9. package/dist/{automatic-outcomes-If88LR9f.d.ts → automatic-outcomes-pAsOyIVp.d.ts} +5 -1
  10. package/dist/avatar.d.ts +1 -1
  11. package/dist/browser.d.ts +15 -11
  12. package/dist/browser.js +15 -11
  13. package/dist/{announcements-CHx3-HBw.d.ts → capability-error-DFMwSuSf.d.ts} +161 -4
  14. package/dist/{pages-K7-KjSXw.d.ts → catalog--ZIlskAo.d.ts} +39 -561
  15. package/dist/{chunk-DYXDZFKA.js → chunk-3KLC4AMA.js} +37 -11
  16. package/dist/{chunk-XBV2CK2H.js → chunk-3PYYGFSB.js} +14 -2
  17. package/dist/{chunk-RHBS5FJL.js → chunk-5TEYFX6X.js} +2 -2
  18. package/dist/chunk-6WCIWXUV.js +31 -0
  19. package/dist/chunk-CICDIBOF.js +373 -0
  20. package/dist/{chunk-MHPKEVEW.js → chunk-CZFXGGEK.js} +5 -0
  21. package/dist/chunk-D26YNACF.js +320 -0
  22. package/dist/{chunk-5I54XBK2.js → chunk-E6IKQHTX.js} +2 -2
  23. package/dist/chunk-FWLQ5XCS.js +21 -0
  24. package/dist/{chunk-SKGTNTLF.js → chunk-GKIFABGN.js} +150 -61
  25. package/dist/chunk-GTDW453U.js +522 -0
  26. package/dist/{chunk-VURRFVNP.js → chunk-HTZHBTIL.js} +69 -27
  27. package/dist/chunk-K2KS3JLY.js +188 -0
  28. package/dist/{chunk-ZDJLPN4L.js → chunk-MK425RXJ.js} +3143 -3209
  29. package/dist/{chunk-L77O64I4.js → chunk-QYPMPWVX.js} +1 -1
  30. package/dist/chunk-UAKYDVT3.js +565 -0
  31. package/dist/{chunk-DHXDBAJF.js → chunk-UPQPUJOU.js} +36 -123
  32. package/dist/{chunk-24I4R732.js → chunk-UW5232GG.js} +34 -19
  33. package/dist/chunk-WSBYWVTZ.js +717 -0
  34. package/dist/{chunk-PPB7HALD.js → chunk-XJFYAXET.js} +9 -8
  35. package/dist/{chunk-7A4J67ZL.js → chunk-XNKSSE32.js} +1 -1
  36. package/dist/{chunk-QEAS4ZGS.js → chunk-Y322OTCH.js} +1 -1
  37. package/dist/{client-C51w7fMb.d.ts → client-B-hbHX30.d.ts} +109 -40
  38. package/dist/{deployments-kWoHFaqj.d.ts → deployments-M4OHn7G3.d.ts} +407 -18
  39. package/dist/identity-integration-3mX6b5iH.d.ts +1241 -0
  40. package/dist/index.d.ts +13 -11
  41. package/dist/index.js +14 -11
  42. package/dist/internal.d.ts +14 -12
  43. package/dist/internal.js +26 -14
  44. package/dist/{offers-BoV4ryUc.d.ts → offers-DhAtss4_.d.ts} +21 -4
  45. package/dist/pages-B8q8Y_dQ.d.ts +715 -0
  46. package/dist/{participant-activity-DHL8QYks.d.ts → participant-activity-D-Drp24G.d.ts} +16 -3
  47. package/dist/portal-proxy.d.ts +59 -2
  48. package/dist/portal-proxy.js +109 -23
  49. package/dist/react/index.d.ts +206 -26
  50. package/dist/react/index.js +153 -25
  51. package/dist/{action-DBzILFx9.d.ts → request-error-D_dUPLwo.d.ts} +1 -31
  52. package/dist/runtime/authoring.js +1 -1
  53. package/dist/runtime/chunks/Y4OAVXFR.js +1 -0
  54. package/dist/runtime/manifest.json +3 -3
  55. package/dist/runtime/schema.js +1 -1
  56. package/dist/schema.d.ts +154 -30
  57. package/dist/schema.js +126 -75
  58. package/dist/server/index.d.ts +14 -12
  59. package/dist/server/index.js +2137 -1633
  60. package/dist/sign-in-providers-BkjcDsFF.d.ts +39 -0
  61. package/dist/submission-input-DHGKhXfp.d.ts +342 -0
  62. package/dist/{telegram-browser-BUceZIGZ.d.ts → telegram-browser-BCE7zdOb.d.ts} +46 -6
  63. package/dist/telegram.d.ts +64 -0
  64. package/dist/telegram.js +23 -0
  65. package/dist/testing.d.ts +112 -15
  66. package/dist/testing.js +321 -16
  67. package/dist/tracker-data-Hb1uQnk5.d.ts +1855 -0
  68. package/dist/{transport-BrX9pIGN.d.ts → transport-DkhX2Cbs.d.ts} +5764 -2217
  69. package/dist/{wallet-CIqFoxdm.d.ts → wallet-DTO7SXj7.d.ts} +20 -7
  70. package/package.json +5 -1
  71. package/dist/accounts-DoabriBX.d.ts +0 -306
  72. package/dist/capability-error-DufwG3G0.d.ts +0 -159
  73. package/dist/chunk-3Y3L2F54.js +0 -982
  74. package/dist/chunk-EN3M5Z4J.js +0 -13
  75. package/dist/chunk-QNUHWK7R.js +0 -602
  76. package/dist/chunk-SE4PFJPT.js +0 -247
  77. package/dist/groups-CqcA2Ycs.d.ts +0 -2827
  78. package/dist/runtime/chunks/5QVFB4TU.js +0 -1
  79. package/dist/submission-input-C54Q8yAD.d.ts +0 -146
  80. package/dist/x-D1PGi8Sq.d.ts +0 -331
package/CHANGELOG.md CHANGED
@@ -1,5 +1,153 @@
1
1
  # @domino-sdk/relay
2
2
 
3
+ ## 1.62.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a341b16: Analytics counts Relay's own `key-used` rejection, of an attempt whose completion key another member's completion holds, as its own rejection source. `rejectionsBySource` gains `keyUsed`, and `analytics.query`'s `source` breakdown a `keyUsed` row; `evaluator` no longer includes them, while an evaluator's own `key-used` still counts there. Each `topReasons` item carries `codeSource` (`relay`, `evaluator`, or null without a code), and Relay's `key-used` and an evaluator's are separate rows, in `topReasons` and in the `reason` breakdown. Both fields are optional in the schema, since older servers don't send them. `domino analytics get` marks the code of a top reason Relay wrote, such as `[relay key-used]` or `[relay budget-exhausted]`.
8
+ - bd622ae: An app login's server can vouch for its members' X, Telegram and Discord accounts and wallets when it signs them in. `createParticipantBackend`'s `authenticate` may return `accounts` next to `subject`, and `onAccounts` receives each account's outcome (`attached`, `already-linked`, or `refused` with a member import refusal code). The new `exchangeParticipant` from `@domino-sdk/relay/portal-proxy` runs the same exchange from a server without a browser and throws `ParticipantExchangeError`, a `RequestError` with the refusal's `status`, `code` and `retryAfter` in milliseconds (`isParticipantExchangeError` recognizes it). The key needs **Vouches accounts**, which `management.identityIntegrations.setAccountVouching(id, { enabled, reason })` turns on from a Console session with publish permission; identity integrations report it as `vouchesAccounts`. Vouched accounts and wallets carry `vouchedBy: { integration, integrationName }` next to `imported: true`.
9
+ - 76be180: An attempt carries `codeSource`: `relay` when Relay wrote its `code`, `evaluator` when the evaluator's `decision.reject({ reason, code })` did, and null without a code (`AttemptCodeSource`, open). `reservedAttemptCodes` no longer grows, so a quest built with any 1.x SDK keeps working when Relay adds a code: `key-used` stays usable as an evaluator's reject code, and `codeSource` tells it apart from Relay's own shared-key rejection. Members still read a rejected attempt's code only when Relay rejected it.
10
+ - 5233be5: The experimental billing results read the plan as `BillingPlan` (`KnownBillingPlan | (string & {})`): `subscription.plan` and `scheduledChange.plan` in `management.billing.read()`, `cancel()`, `resume()` and `organization()`, `billingSubscriptionSchema`, `projectBillingSchema` and `organizationBillingSchema`, and the `--json` results of `domino billing show`, `billing organization`, `billing cancel` and `billing resume`, so a plan Relay adds no longer fails to parse. Handle a plan name this SDK does not know. The stable `management.usage.plan()` and `domino usage plan` are unchanged: the `subscription` they carry keeps the plans this SDK knows (`launch`, `growth`, `scale`).
11
+ - 3665a4e: Billing is experimental while it is open to Domino staff only: the `management.billing` methods, the billing schemas and types in `@domino-sdk/relay/server`, and the `domino billing` commands may change in a minor release, plan names included. `domino billing` now appears under **Experimental commands**. `management.billing.checkout()` and `changePlan()` take any plan name (`BillingPlan`, `KnownBillingPlan | (string & {})`), and `domino billing checkout` and `billing change` take any `--plan`; Relay refuses a plan it does not offer with 400, as before. `management.usage.plan()` and `domino usage plan` stay stable.
12
+
13
+ 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 `oneOf` stay closed. `webhooks deliveries`, `storage rows` and `storage keys` results declare the optional `nextCursor` the API already sent. `secrets`, `storage` and `schedules` results include `environment`, and these commands warn on stderr when `RELAY_ENVIRONMENT` or `relay.json` names live while they use test. `contract.json` marks deprecated options with `deprecated`, as it does commands, and `capabilities.json` no longer lists deprecated commands. `management.referrals.control` and `referralControlSchema` now point to `pause` and `resume` instead of the deprecated `suspend`.
14
+
15
+ - 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) or `unchanged`. `domino deploy` lists the created and updated quests in its confirmation and in the `--preview --json` summary, so a deploy that would replace a quest you didn't touch shows before it publishes.
16
+ - 154af63: Discord message and reaction records name a thread's parent channel: `DiscordMessageData` and `DiscordReactionData` gain `parentChannelId`, the channel a thread belongs to (a forum post's forum), or `null` outside a thread. It is absent when it is unknown, because Discord's channel read failed. `channelId` stays the thread's own ID. A trigger's `where: { path: "parentChannelId", equals }` selects a channel's threads; to count a channel and its threads, compare both fields in the evaluator. `management.testMembers.record` and `domino test-members record --data` take `parentChannelId` for Discord records.
17
+ - 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 with `key-used` and `codeSource: "relay"`, which members see; `attempts.acceptAnyway` refuses it with 409 `key-used` while the key is held. Revoking the holder's completion with `freeOccurrence: true` frees the key. Member imports refuse a row with `MemberImportRefusalCode.KeyUsed` when another member or an earlier row already uses one of its keys.
18
+ - 56a0601: `fakeEvaluationMember(methods)` from `@domino-sdk/relay/testing` builds an `EvaluationMember` for 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, and `balance` and `referrals` fail with a `CapabilityError` of code `author-error`. A member written by hand stops compiling when a minor release adds a method; one from `fakeEvaluationMember` does not.
19
+
20
+ The CLI's automation contract (`cli/contract/contract.json`) lists the JSON Schemas of `relay.json` and the checkout's `domino.json` under `files`, so a later release is checked to accept every file an older CLI wrote.
21
+
22
+ - 5cc0d4d: Revoking a completion with `freeOccurrence: true` now 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. `CompletionRevocation` reports this in a new optional `budgets` (`budget`, `released`). Revoking without freeing the occurrence leaves budgets spent, as before. Completions recorded before this release give no budget back and report `budgets: []`.
23
+
24
+ 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 409 `budget-exhausted`, naming the `budget`, 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.
25
+
26
+ - f9c9f34: `defineOffer`, `defineLeaderboard` (prizes) and `defineReferral` (inviter, invitee and milestone rewards) now throw when given a `badge()` or `groupMembership()` reward, so `domino check` reports it before a deploy. These definitions type-checked but a deployment always refused them; now they fail when defined, with `Badge rewards can only be granted by quests or manually, not by offers` (or `leaderboard prizes`, `referral programs`), or `Group membership rewards can only be granted by quests, not by …`. A deployment refuses them with the same message and 422 `validation-failed`, instead of claiming a tier reward was needed. `badgeReward` and `groupReward` from `@domino-sdk/relay/schema` are deprecated: use `badge()` and `groupMembership()` from `@domino-sdk/relay/authoring`. `defineOffer` from `@domino-sdk/relay/schema` is deprecated too and keeps its old behaviour; import it from `@domino-sdk/relay/authoring`, which does the check.
27
+ - dcc32da: `ctx.wallet.events()` takes `newest`, 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 throwing `author-error`. `WalletEvents` gains an optional `complete`, `false` when a `newest` read left older matching events out or stopped before `since`. 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 without `newest` behaves as before: it returns every matching event, now with `complete: true`, or throws `author-error`. A read whose start-block estimate overshot 3,000,000 blocks while the span itself fit is no longer refused. The `onchain-action` snippet reads with `newest: maxWalletEventLogs`.
28
+ - 98ad384: A member import carries its real state in a new `state` field: `open`, `checking`, `checked`, `applying`, `applied`, `stopped`, `reverting`, `reverted` or `canceled`, typed open (`KnownMemberImportState | (string & {})`, with `MemberImportState` listing the known values), so Relay may add states. `status` is unchanged and still reads `applied` for a stopped, reverting or reverted import, so code filtering on `status === "applied"` counts those too; read `state` instead. Import rows gain `reverted`, 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 import` and `domino imports` results include `state`, and the CLI polls on it.
29
+
30
+ `MemberSummary` (`relay.me()`, `useMe()`) now types `email`, `emails` and `clock` as required, since Relay always sends them: code no longer needs to check them for `undefined`. The SDK reads a `/v1/me` response from a Relay older than these fields with `email: null`, `emails: []` and a `clock` at real time (offset 0), the same as code treating them as absent did. `memberTime()` still accepts an object without `clock`.
31
+
32
+ `MemberSummary`, `LeaderboardPage`, `LeaderboardHistoryPage`, `Badge`, `MemberBadge` and `MemberBadges` are marked as response types Relay may add properties to.
33
+
34
+ - 4bf82e5: More management writes are safe to retry. `memberImports.check`, `apply`, `cancel` and `stop`, and `promotions.approve`, take an optional `{ actionId }` before the call options (`check(id, { actionId })`; `check(id, call)` still works), and so does `webhooks.replay(id, delivery, { actionId })`. `promotions.reject`, `members.unlinkCreatorAccount` and `identityIntegrations.setAccountVouching` take `actionId` in 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.
35
+
36
+ Inputs fenced by `revision` also take it as `expectedRevision`, the name newer methods use: quest and redemption `pause` and `resume`, `offers.control`, `offers.cancelRedemption`, `handover.begin` and `cancel`, `entitlements.void`, `restore` and `correctCollection`, the pickup policy, `members.unlinkCreatorAccount` and the deprecated account unlinks. Pass either; a call that names two different revisions is refused. `expectedRevision()` and the `ExpectedRevision` type are exported from `@domino-sdk/relay/server`.
37
+
38
+ `attempts.acceptAnyway`, `attempts.redactEvidence`, `completions.revoke` and `completions.restore` take their path ID first, as the route names it: `acceptAnyway(attempt, { reason })`. The object form, `acceptAnyway({ attempt, reason })`, keeps working.
39
+
40
+ - 4bf82e5: `relay.badges.catalog()` reads the project's active badge catalog as `{ items }`, like `relay.me.badges()`, from the new `GET /v1/badges/catalog`. `relay.badges.list()` and `GET /v1/badges` keep answering the bare array and are deprecated. `useBadgeCatalog()` now reads the new route and still returns the array. `createFakeRelay` takes a `badges` seed and answers both routes from it.
41
+
42
+ `relay.auth.exchange()`, `ExchangeInput` and `exchangeSchema` are deprecated: `POST /v1/auth/exchange` refuses 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 with `exchangeParticipant` from `@domino-sdk/relay/portal-proxy`.
43
+
44
+ The activity feed's JSDoc, and `paginate()`'s, now say that `me.activity.list` never answers a null `nextCursor`, so a loop over it stops on its own condition, such as an empty page or a count.
45
+
46
+ - 4bf82e5: ISO times in management responses carry their epoch milliseconds beside them, named `<field>Ms`. `analytics.read` and `analytics.query` (and so each `dashboards.read` panel) send `range.fromMs`, `range.toMs`, each bucket's `startMs` and `endMs`, `previous.fromMs` and `previous.toMs`, and `testEnvironment.lastResetAtMs`; custom domains (`management.domains.list|get|add|recheck`) send `checkedAtMs`, `nextCheckAtMs`, `createdAtMs` and `cloudflare.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 })` takes `to` as epoch milliseconds as well as an ISO 8601 instant with an offset; `testMemberClockAdvanceSchema` parses epoch milliseconds to that instant as an ISO string in UTC, so its parsed `to` stays a string.
47
+ - ea2c9be: A retention job can delete the evidence of a quest's attempts decided before a cutoff: `management.attempts.redactEvidenceBefore({ quest, decidedBefore, reason })` (`review` permission; in live it also requires an organization administrator's Console session or personal key, or a service key, and refuses any other caller with `403 permission-required`, because the deletion can't be undone) deletes up to 100 attempts' evidence per call, oldest decision first, exactly as `attempts.redactEvidence` does for one, and answers `more: true` while others remain. Attempts still waiting for a decision are skipped, and a `decidedBefore` in the future is refused with `400 invalid-request`. Each call records one `evidence-redacted-before` audit event. `domino attempts redact-evidence-before --quest QUEST --decided-before MS --reason TEXT` repeats the call until nothing is left and prints the total.
48
+ - 2de5d71: `@domino-sdk/relay/testing` adds `createFakeRelay(options)`, an experimental in-memory participant API for UI tests of apps built on `@domino-sdk/relay/react`. Seed it with a `member` (or `null` for signed out), `quests`, `balances`, `activity`, `leaderboards` and 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 returns `handle(request)` for a Playwright or MSW route handler, `fetch` for `createRelayClient`, and `requests`, every request it answered. Each answer is checked against the SDK's response schema, so a seed Relay could never send throws a `TestSetupError` naming the route. Another participant route answers `501` with code `not-faked` until `routes` answers it. The testing guide lists the requests `RelayProvider` makes on mount and while polling. `createFakeRelay` and its `FakeRelay`, `FakeRelayOptions` and `FakeRelayRequest` types are tagged `@experimental`, outside the 1.x compatibility promise: a minor release can change them.
49
+ - 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`).
50
+
51
+ - `members.identityStates.unlink` and `relink` take it as `identity` (`memberIdentityStateChangeInputSchema`, `MemberIdentityStateChangeInput`); `setSignIn` also takes a Cosmos wallet with its `network`. 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 409 `identity-ambiguous`; name it by `issuer` then.
52
+ - `members.lookup` takes it, and sends it as the `provider` and `subject` every Relay version reads.
53
+ - `testMembers.linkAccount` and `unlinkAccount` take `{ actionId, kind, subject }` for an X or Discord account.
54
+ - The CLI takes `--kind` and `--subject` on `domino members lookup` (with `--chain` and `--address` for a wallet) and on `domino test-members link-account` and `unlink-account`.
55
+
56
+ Deprecated, removed in 2.0.0: the `{ kind: "oauth", provider, issuer, subject }` and `{ kind: "app", appKind, subject }` input selectors, `memberIdentityLinkChangeInputSchema` and `MemberIdentityLinkChangeInput`, `members.lookup({ provider, subject })`, the test-member `{ provider, id }`, and the CLI's `members lookup --provider` and `test-members link-account|unlink-account --provider|--id`. They keep working through 1.x; `contract.json` marks the deprecated options with `deprecated`.
57
+
58
+ Identity reads gain an optional `source`: `member`, `import`, `app-login` or `test-link`, an open string (`IdentitySource`, `KnownIdentitySource`), and `import`, the ID of the member import that attached the identity when Relay recorded it. Identity states of every kind carry them, and so do `members.get`'s `wallets` and `identities` and the deprecated `oauthAccounts` and `telegramAccount`. `imported` is unchanged.
59
+
60
+ - 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 with `settle.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.
61
+
62
+ `ties` is optional. Omitted or `"shared"`, equal scores share a rank as before, so published boards rank unchanged. It is independent of `settle.ties`; set both on a board that settles so members see the order prizes follow. Members listed through `includeUnscored` still share one rank after every member who scored.
63
+
64
+ - 7a93f76: `createParticipantBackend` from `@domino-sdk/relay/portal-proxy` accepts a loopback HTTP app origin (`http://localhost`, `http://127.0.0.1` or `http://[::1]`, any port), so a browser test can run the real adapter, identity exchange included, at a local address against `createFakeRelay`'s `fetch`. On such an origin the `domino_app_session` cookie omits `Secure`. Any other `http:` origin still throws, `baseUrl` must still be HTTPS, and Relay still registers only HTTPS app origins.
65
+ - 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()` and `members.get()` list each member's linked `appIdentities` (`{ kind, subject }`), and `members.list({ q })` also matches part of an app identity's subject. Identities an operator unlinked are neither found nor listed.
66
+ - dd7ced3: Experimental: a member import row can carry the member's marketing consent from the platform they come from. The optional `consent` list on `memberImportRowSchema` takes one `{ channel, state: "granted" | "withdrawn", at, wording? }` per channel; applying the row records it with its original time and wording, marked `source: "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 with `future-action` for a time in the future, `unsupported-consent-channel` for a channel other than `email`, `consent-wording-required`, `consent-wording-too-long` or `consent-wording-invalid` for the wording, and `duplicate-entry` for a channel named twice. `KnownConsentSource` gains `imported`. The CLI imports reference describes the field.
67
+ - 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`), and `relay.consent.set({ channel, granted, wording, actionId? })` grants or withdraws one channel. The `useConsent()` React hook returns the query with `grant(channel, wording)` and `withdraw(channel, wording?)`. Relay stamps the time and stores the wording verbatim; a repeat of the current state and wording records nothing. Only `email` is accepted for now: other channels are refused with `unsupported-consent-channel`, a grant without wording with `consent-wording-required`, wording over 2,000 characters with `consent-wording-too-long`, and wording with a NUL or a lone surrogate with `consent-wording-invalid`. Channel, state and source are open strings in results. The consent API is `@experimental` and can change in a minor release. The participant API reference and `capabilities.json` describe it.
68
+ - 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.
69
+
70
+ - `defineQuest` accepts an integration module whose provider the SDK does not know, such as `{ provider: "future-chat" }`. `IntegrationModule.provider` is `IntegrationModuleProvider`, `KnownIntegrationModuleProvider | (string & {})`. The quest builds; its description keeps `integrations` to the modules the SDK knows and adds `integrationModules`, every declared module. Publishing a quest that declares a module Relay does not have is refused with 422 `unknown-integration-module`. Discord and X quests behave as before. Evaluator contexts type readers only for the modules the SDK knows, now named `KnownIntegrationModule`: `EvaluationContext`'s module parameter takes `readonly KnownIntegrationModule[]`, so a helper that passed `readonly IntegrationModule[]` to it explicitly passes `readonly KnownIntegrationModule[]` instead.
71
+ - Release reads (`management.releases.get`, `releases.list`, publish results and every other `PublishedRelease`) add `integrationModules` and `rewardEntries`. `rewardEntries` lists every reward as a `RewardEntry`, `{ type, data }`, with an open `RewardType`; a reward kind the SDK does not know, which a later Relay adds, will appear only there and never in `rewards`. `management.quests.previewAutomaticCatchUp()` adds the same `rewardEntries` beside `rewards`. Older Relay versions omit these fields.
72
+ - `ActivityType` (`KnownActivityType | (string & {})`) from `@domino-sdk/relay/server` names the webhook and activity event types for your own handlers; `WebhookEvent.type` stays a `string`. Ignore a type you don't handle: Relay adds event types.
73
+
74
+ - de99154: Every sign-in provider Relay has, Telegram included, now has open surfaces beside the closed `AuthProvider` lists, which keep their exact shapes. `SignInProvider` is `KnownSignInProvider | (string & {})`, so handle a provider you do not know.
75
+
76
+ - `relay.auth.providers()` adds `signInProviders`, `[{ provider, configured }]` for every sign-in provider. `management.participantSignIn()` adds the same `signInProviders`. Older Relay versions omit both.
77
+ - `relay.auth.signInAccounts()` (`GET /v1/auth/sign-in-accounts`) returns the member's account on every sign-in provider as `SignInAccount`s, shaped like `AccountConnection` with an open `provider` and `status`.
78
+ - `relay.auth.start({ provider, mode })` and `relay.auth.disconnect(provider)` take any `SignInProvider`, such as `"telegram"`.
79
+ - `useAccount(provider)` from `@domino-sdk/relay/react` reads one provider's account, with `connect()`, `disconnect()` and `isConnected`.
80
+ - `management.members.accounts(member)` (`GET /members/:member/accounts`) returns `{ accounts }`, the member's X, Discord and Telegram selections as `ManagedAccount`s with an open `provider`. `management.members.unlinkAccount(member, { provider, subject, expectedRevision, reason, actionId? })` (`POST /members/:member/accounts/unlink`, `review` permission) unlinks one and returns that provider's selection; a retry with the same `actionId` returns the first result.
81
+
82
+ - 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, and `management.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 }`, and `memberConsentHistorySchema` and `memberConsentHistoryQuerySchema` are exported from `@domino-sdk/relay/schema`. Both need `read` permission (`GET /management/v1/members/:member/consent` and `/consent/history`); a member who never chose reads as empty, and an unknown member fails with 404 `not-found`. `domino members consent MEMBER` prints both, with `--limit` and `--cursor` for 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 `@experimental` and can change in a minor release.
83
+ - 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 (422 `invalid-config` names the failing `path`), its secrets are write-only and sealed at rest, every change takes an `actionId`, and `update` and `disconnect` take an `expectedRevision` (409 `connection-changed`). The new exports (`connectionSchema`, `connectionPageSchema`, `Connection`, `ConnectionStatus` and the connect, update and disconnect inputs) are experimental and may change in a minor release. The CLI adds the experimental `domino connections list|get|connect|update|disconnect`, test unless `--environment live`, reading secrets from stdin as JSON or at hidden prompts, never from arguments.
84
+ - 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`, `KnownPluginSlot` and `knownPluginSlots`) are experimental and may change in a minor release. Console builds each plugin's connection form on **Integrations** from it.
85
+ - 2a83792: Plugin OAuth connections, experimental. A plugin whose `plugins.list()` entry has `oauth` connects 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 connection `ready`. 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 is `attention` with the new `reconnectReason` (`revoked` or `expired`), and authorizing again reconnects it. Disconnecting deletes the tokens, then revokes them at the vendor; a failed revocation is logged. `connect` refuses an OAuth plugin with 409 `oauth-required`, `authorize` refuses one connected with secrets with 409 `oauth-not-supported`, and a deployment without the vendor's OAuth app with 409 `plugin-not-configured` (`oauth.configured` is false). New experimental exports: `connectionAuthorizeInputSchema`, `connectionAuthorizationSchema`, `ConnectionAuthorizeInput`, `ConnectionAuthorization`, `ConnectionReconnectReason`, `KnownConnectionReconnectReason` and `knownConnectionReconnectReasons`. The CLI adds the experimental `domino connections authorize <plugin> --config-json …`, which prints the authorization URL.
86
+ - 172c3b5: Experimental: a `viewer` project role and grant end dates, to share a project's dashboards with a sponsor or client. A viewer holds one permission, `dashboards`, which reads `dashboards.list` and `dashboards.read` and nothing else: no members, evidence, quests or settings. Every other role holds it too, and `read` keeps reading dashboards, so no existing person or key loses access.
87
+
88
+ `management.grants.set` takes `projectGrantSchema`: `roles` may name `viewer`, and an optional `expiresAt` (epoch ms, or null for no end) ends the grant's access without anyone removing it. An ended grant stays in `grants.list()`, and the access history records each end date. Released SDKs keep reading grants: a grant's `roles` keeps the roles 1.x names, so a viewer's is empty, and the new `projectRoles` lists every role. Likewise a project in `management.access()` keeps `dashboards` out of `permissions` and lists it in the new `projectPermissions`. `projectRoleSchema`, `projectPermissionSchema`, `projectRolePermissions`, `projectGrantSchema` and these fields are experimental.
89
+
90
+ 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.
91
+
92
+ - b2b8127: A full deployment to test warns `quests-not-in-bundle` with 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 with `domino quests archive <key> --reason "…"`. Archived quests and quests created in Console are not listed. A deployment input with `partial: true` doesn't warn, and `domino deploy --only` sends it. `domino stage` now prints the warnings of the deployment its build published, and its `--json` result carries them in `warnings`; a build's status (`GET /builds/{id}`) names that deployment in `deployment`.
93
+ - a381dde: Staff can delete the evidence a member submitted for a decided attempt: `management.attempts.redactEvidence({ attempt, reason })` and `domino attempts redact-evidence ATTEMPT --reason TEXT` (`review` permission, 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.get` answers the new `redaction` (`{ at, actor, reason }`). Undecided attempts are refused with `attempt-not-decided`, and `attempts.retry` refuses a redacted attempt with `evidence-redacted`.
94
+ - 4443124: Quest code can read whether the member linked Telegram: `ctx.telegram.connection()` resolves to `{ userId }`, the numeric Telegram user ID, or `null`. 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, like `member()`, and is captured for replay. The testing kit answers it from `facts.telegram.connection`, and `TelegramConnection` is exported from `@domino-sdk/relay/authoring`.
95
+ - 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:
96
+
97
+ - `relay.auth.telegram.start(mode)` → `relay.auth.start({ provider: "telegram", mode })`.
98
+ - `relay.auth.telegram.account()` → the `"telegram"` entry of `relay.auth.signInAccounts()`.
99
+ - `relay.auth.telegram.disconnect()` → `relay.auth.disconnect("telegram")`.
100
+ - `useTelegramAccount()` → `useAccount("telegram")`. Inside a Mini App, link with `relay.auth.telegram.miniApp.connect(initData)`, then call `invalidate([relayKeys.accounts, relayKeys.experience])` from `useInvalidate()`.
101
+ - `useSignIn().withTelegram()` → `useSignIn().withProvider("telegram")`; `withProvider` now takes any `SignInProvider`.
102
+ - `SignInOptions.telegram` and `useSignInOptions().telegram`, `TelegramSignInOption` and `SignInActions.telegram` → `signInProviders`, below.
103
+ - `relay.auth.providers().telegram.login` and `management.participantSignIn().telegram.login` → the `"telegram"` entry of `signInProviders`. `TelegramSignInMethods` and the new `ParticipantSignInTelegram` are written-out types, so editors show these two deprecations.
104
+ - `management.members.telegramAccount(member)` and `unlinkTelegramAccount(member, input)` → `members.accounts(member)` and `members.unlinkAccount(member, { provider: "telegram", subject, expectedRevision, reason })`.
105
+
106
+ `useSignInOptions()` and `signInOptions(relay)` add `signInProviders`, the open sibling of the `oauth` entries of `options`: every sign-in provider Domino configured, Telegram included, in Relay's order, each a `ProviderSignInOption` (`{ 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 without `signInProviders`, the list holds its providers and Telegram Login. `SignInMethods` adds `signInProviders` and `SignInActions` an optional `provider(provider)` action. The starters render their provider buttons from `signInProviders`.
107
+
108
+ Mini App sign-in (`relay.auth.telegram.miniApp`, `useSignIn().withTelegramMiniApp`, `providers().telegram.miniApp`, `participantSignIn().telegram.miniAppBotId`) is not deprecated.
109
+
110
+ - 90a20c6: Telegram is now a plugin: `management.connections.list()` and `domino connections get telegram` include it, `ready` while the environment has a connected chat, `attention` with the chat's issue while Domino's bot can't read one, and `disconnected` without chats. Chats still connect only through the bot (`management.telegram.connect()`, unchanged), so `connections.connect`, `update` and `disconnect` answer 409 `plugin-managed` for Telegram and for any plugin that connects through its own flow. `knownPluginSlots` adds `announcement`, the slot Telegram posts quest announcements through. `management.plugins.list()` items gain `console`: `{ 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.
111
+ - 8e8e24a: Telegram Mini App sign-in has its own entrypoint, `@domino-sdk/relay/telegram` (experimental): `withTelegram(relay)` adds `relay.telegram` to a client and answers the same client, `relay.telegram.miniApp.signIn(initData)` and `.connect(initData)` do what `relay.auth.telegram.miniApp` did, and `telegramMiniAppInitData()` reads the launch data. Apps that don't import it don't carry it. `relay.auth.telegram.miniApp` and the `telegramMiniAppInitData` exports of `@domino-sdk/relay/browser` and `@domino-sdk/relay/react` are deprecated, keep working through 1.x and are removed in 2.0.0.
112
+
113
+ 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-app` takes the same requests. `management.plugins.list()` and `domino connections get telegram` list one more slot for Telegram, which `knownPluginSlots` adds: `identity` (members sign in with it).
114
+
115
+ - 52ed037: Telegram's trackers and `ctx.telegram` now come from the Telegram plugin, with no change to what they record or read: `telegram.messages()` and `telegram.reactions()` trackers, the bot's webhook at `/v1/integrations/telegram/updates`, and `ctx.telegram.connection()` and `member()` behave as before, and published quests keep working. `management.plugins.list()` and `domino connections get telegram` list two more slots for Telegram, which `knownPluginSlots` adds: `tracker-source` (the plugin writes its trackers' records) and `evaluator-capability` (quest code reads it as `ctx.<plugin>`). A quest may now declare the `telegram` integration module, `{ provider: "telegram" }`, and publishes; before, publishing refused it with `unknown-integration-module`. Declaring it is optional, since every quest gets `ctx.telegram`.
116
+ - 2fe2d72: `management.testMembers.history()` and `domino test-members history` name each attempt's and skipped tracker outcome's quest by its authoring key as `questKey`, next to the quest ID in `quest`. It is absent for a quest created without a key. A script can filter a test member's history by quest key without calling `quests get` first.
117
+ - 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, and `act` submits its `id` as `evidence`. `domino test-members act MEMBER QUEST --photo FILE` does 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 at `facts.images.review`.
118
+ - 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 and `beneficiary: "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" })` with `beneficiary: "reposted"`. `xAccountRoles` gains both. A post that is no quote or repost is skipped with `no-account` ("Post is not a quote" / "Post is not a repost"), and a quote or repost of the author's own post with `beneficiary-is-author`. Like `inReplyTo`, these quests don't reward posting and don't raise `x-post-rewards-ungated`. `xAccountRoles` and `SourceRoles` list the roles this SDK knows, and a later release may add roles to them, so code that switches over them needs a default branch.
119
+
120
+ ### Patch Changes
121
+
122
+ - ee3f488: Bulk management writes (`catalog.deploy`, catalog previews and drafts, `deployments.preview`, `releases.publishBatch`, tracker record ingest and `memberImports.addRows`) refuse a body with more than 2,000,000 JSON values with 413 `payload-too-large` before 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 with `Retry-After` when it has waited 10 seconds. `memberImports.addRows` now 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 import` now 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.
123
+
124
+ 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.
125
+
126
+ - f13cfb6: A `domino login` CLI session creates and scopes personal API keys in test only. `management.tokens.create` with `environment: "live"`, and `management.tokens.scope` on a live key, now answer 403 `console-approval-required` from a CLI session, whatever `X-Relay-Environment` selects: a live key would carry the live operations (points, attempt confirmations, reviews, pauses) that the CLI session itself is refused, and would outlive `domino 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.
127
+
128
+ 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 live` prints that refusal, and `domino tokens --help` says live keys come from Console.
129
+
130
+ - eed1808: Participant cookies on HTTPS now use `__Host-` names (`__Host-relay_session` and the sign-in binding cookies), so one app on `domino.page` can no longer set them for another, and a cookie name the browser sends twice reads as absent. `createParticipantBackend` forwards the OAuth return binding as `__Host-relay_auth_return` when the browser has it, otherwise `relay_auth_return`, and forwards neither when a name appears twice.
131
+
132
+ 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`.
133
+
134
+ - 94ca2b5: Unlinking an imported or vouched account now releases it. `management.members.identityStates.unlink` (and `domino members unlink-identity` and 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 with `canRelink: false`, and later imports and app-login vouching refuse it for that member with `identity-unlinked`. Accounts the member signed in with or proved keep the previous behaviour: the link is cleared, ownership stays, and it can be relinked.
135
+ - d4fcade: Role delivery helpers ignore announcement rows. `useExperience().deliveries`, `roleDeliveryStatus` and the watch behind them count only `grant-role` deliveries, 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. `roleDeliveryStatus` applies the same rule to raw `me.rewardDeliveries()` rows, and `RoleDelivery` gains an optional `action`. `me.rewardDeliveries()` itself still returns every delivery.
136
+ - 8064983: Deployment previews, records and history pages (`deploymentPreviewSchema`, `deploymentRecordSchema`, `deploymentHistoryPageSchema`, and the `management.deployments` methods that return them), `collectionSlotSchema` and `questCollectionSchema` read collections without rejecting unknown fields, so a field a newer Relay adds to a deployment collection no longer fails the response. Collection inputs (`collectionSlotDeclarationSchema`, `catalogDeploymentSchema` and saved draft changes) still reject unknown keys.
137
+ - 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 login` device 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.
138
+ - 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.
139
+ - 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, `git` and the MCP server: after 30 in 10 minutes from one IPv4 address or IPv6 /64, counted by each API server on its own, every `ak_` and MCP token from it answers 429 `rate-limited` with `Retry-After` until 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 401 `invalid-credential` without asking Clerk. When Domino is checking many new credentials at once, a key or token it has not accepted recently can answer 503 with `Retry-After: 1`. Clerk's own rate limit now answers 503 with Clerk's `Retry-After`.
140
+ - 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.
141
+ - 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.
142
+ - 10826dc: An organization administrator's CLI session lists the project's identity integrations (app login keys) and their claimed kinds: `domino identity-integrations list` and `domino identity-integrations kinds`, or `management.identityIntegrations.list()` and `kinds()`. 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.
143
+ - c391c86: Documentation only: the JSDoc for `pointLedgers.create`, `pointLedgers.update`, `calendar.configure`, `profiles.configure` and the `campaigns` methods now says what live refuses. In live, every definition write is refused for every credential, Console included, unless it applies an approved promotion; `campaigns.enroll` and `campaigns.withdraw` are operations that need `enroll` and work in live with a personal API key.
144
+ - 1c61073: `domino tokens list|create|scope|revoke` manage your personal API keys from a `domino login` session, as `management.tokens` does. `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-time `token` and expiry; `--json` prints the SDK's answer, so `domino tokens create … --json | jq -r .token | domino secrets set NAME` stores it. A personal key expires after `days`, 1 to 90, default 30; `--days 91` is refused before anything is sent. `domino tokens list` shows 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.
145
+ - 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/submit` and `POST /v1/attempts/:id/retry`. Past it they answer 429 `rate-limited` with `Retry-After`, before the request is read. The quest controllers keep the saved draft, so `resume()` continues it. The participant reference in the Domino skill states the limit.
146
+ - 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. `earned` in the referral summary and the management referral accounts excludes the reversed points, so a reversed payout no longer reads as earned.
147
+ - 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 makes `referrals.accept` and `testMembers.acceptReferral` refuse with `invitation-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.
148
+ - 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-found` before any build is reserved, instead of an uncertain 502 `upstream-failed`. `domino stage` reports it as `REJECTED` with `apiCode: "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.
149
+ - 3d2f654: Staging's participant API is now private, like staging itself. A participant request that names a staging address (`<slug>--staging.domino.page`) in `X-Relay-App-Origin` answers only when it carries the staging pass, which the staging gateway gives browsers signed in to staging; anything else is refused with 403 `staging-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, and `proxyParticipant` forwards 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 retired `staging-<slug>.domino.run` origin now selects nothing (403 "Application origin is not registered or is offline"). Previews, live apps and identity integrations are unchanged.
150
+
3
151
  ## 1.61.0
4
152
 
5
153
  ### Minor Changes
@@ -971,7 +1119,7 @@ No changes in this release.
971
1119
 
972
1120
  Stable APIs follow semantic versioning throughout the supported v1 lifetime. Compatibility exemptions apply only to explicitly experimental entrypoints.
973
1121
 
974
- - 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 [Remove stored pre-v1 state before cutover](https://console.domino.run/docs/reference/upgrade-to-v1#remove-stored-pre-v1-state-before-cutover) in the v1 upgrade guide.
1122
+ - 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](https://console.domino.run/docs/reference/upgrade-to-v1#wallet-reads-return-every-linked-wallet) in the v1 upgrade guide.
975
1123
  - bdb89a4: Evaluator readers follow one rule: a missing subject is `null`, a failure throws `CapabilityError`, 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.
976
1124
 
977
1125
  - **Missing is `null`.** `ctx.x.profile()`, `follows()` and `reposted()` return `null` without a linked X account instead of throwing; `ctx.x.post()` returns `null` for a post that does not exist and needs no linked account. `ctx.discord.member()` returns `null` without a linked Discord account. `ctx.wallet.list()` returns `null` instead of `[]`, and `ctx.wallet.balance()` returns `null` instead of `total: "0"` when the member has no wallet of that family. `ctx.trackers.content()` returns `null` for a missing record or observation. See [Readers return `null` when an account is not linked or a subject is missing](https://console.domino.run/docs/reference/upgrade-to-v1#readers-return-null-when-an-account-is-not-linked-or-a-subject-is-missing).
@@ -993,7 +1141,7 @@ No changes in this release.
993
1141
 
994
1142
  - 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](https://console.domino.run/docs/concepts/quests#distinct-action-counters). Update custom attempt renderers using the [counter upgrade steps](https://console.domino.run/docs/reference/upgrade-to-v1#counter-progress).
995
1143
  - bac9ca4: Declare provider modules to enable evaluator readers, use `discord.grantRole()` for role rewards, and replace Solana-only wallet readers with `wallet.list()` and `wallet.balance()`. See the [upgrade guide](/docs/reference/upgrade-to-v1#declared-integrations).
996
- - 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, and `domino webhooks` commands. This is additive; existing integrations need no changes. See [Outbound webhooks in the v1 upgrade guide](https://console.domino.run/docs/reference/upgrade-to-v1#outbound-webhooks) for an optional polling migration.
1144
+ - 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, and `domino webhooks` commands. This is additive; existing integrations need no changes. See [Receive outbound webhooks](https://console.domino.run/docs/build/webhooks) for an optional polling migration.
997
1145
  - 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`, `handover` permission) returns a handover in progress to `available` and puts its pickup size back into stock. `management.entitlements.correctCollection` (`…/correct`, `review`) does the same for a collection recorded by mistake, and `management.entitlements.void` (`…/void`, `review`) voids an available entitlement. A stale revision fails with 409 `entitlement-changed`, the wrong starting status with 409 `entitlement-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 --json` lists them as `entitlement-corrections`.
998
1146
 
999
1147
  Breaking: `Entitlement` gains the status `"void"`, with `voidedAt`. A voided entitlement cannot be collected; a quest that grants the same reward to the member again makes it `available`, 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 tolerant `entitlementSchema` (fields a newer server adds are kept, and an unknown status reads as `"unsupported"` with `reportedStatus`); the strict stored shape is `entitlementStateSchema`, and the management client's `entitlementRecordSchema` is removed in favor of `entitlementSchema`. To upgrade, see [Entitlements can be `void`, and reads tolerate statuses they do not know](https://console.domino.run/docs/reference/upgrade-to-v1#entitlements-can-be-void-and-reads-tolerate-statuses-they-do-not-know).
@@ -1020,7 +1168,7 @@ No changes in this release.
1020
1168
 
1021
1169
  - c320586: Replace untyped prerequisite objects with typed rules. `completed(questHandle)` creates a completion rule with `scope: "ever"`; `completed(questHandle, { scope: "period", period: "day" })` checks the current project-calendar period. Balance and group requirements use `balanceAtLeast(ledgerHandle, minimum)` and `inGroup(groupHandle)`. Persisted rules carry an explicit `kind`; bare `{ quest }` and string prerequisites are not canonical v1 input.
1022
1170
 
1023
- See [Prerequisites no longer carry an untyped scope](https://console.domino.run/docs/reference/upgrade-to-v1#prerequisites-no-longer-carry-a-scope) and [Authoring references use typed handles](https://console.domino.run/docs/reference/upgrade-to-v1#primitive-handles) in the upgrade guide.
1171
+ See [Prerequisites are typed eligibility rules](https://console.domino.run/docs/reference/upgrade-to-v1#prerequisites-are-typed-eligibility-rules) and [Authoring references use typed handles](https://console.domino.run/docs/reference/upgrade-to-v1#primitive-handles) in the upgrade guide.
1024
1172
 
1025
1173
  - 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](https://console.domino.run/docs/reference/upgrade-to-v1#discord-role-ownership-and-reconciliation) before upgrading.
1026
1174
 
@@ -1029,7 +1177,7 @@ No changes in this release.
1029
1177
  Publish initial pending delivery events in the grant transaction so observers can see queued rewards before reconciliation starts.
1030
1178
 
1031
1179
  - 8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
1032
- - 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 [upgrade guide](https://console.domino.run/docs/reference/upgrade-to-v1#explicit-resource-archive-and-restore).
1180
+ - 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](https://console.domino.run/docs/concepts/quests#resource-lifecycle).
1033
1181
  - ddfc526: The participant client, the management client and the CLI share one set of deadline, cancellation and retry rules.
1034
1182
 
1035
1183
  - 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, as `timeout` already does for the management client. A timeout is never retried.
@@ -1106,7 +1254,7 @@ No changes in this release.
1106
1254
 
1107
1255
  **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.
1108
1256
 
1109
- - 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 [Remove stored pre-v1 state before cutover](https://console.domino.run/docs/reference/upgrade-to-v1#remove-stored-pre-v1-state-before-cutover) in the v1 upgrade guide.
1257
+ - 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](https://console.domino.run/docs/reference/upgrade-to-v1#wallet-reads-return-every-linked-wallet) in the v1 upgrade guide.
1110
1258
 
1111
1259
  ### Minor Changes
1112
1260
 
@@ -1120,7 +1268,7 @@ No changes in this release.
1120
1268
  Keep private operator revocation reasons out of participant reward, progress and experience reads while retaining visible collection recovery state.
1121
1269
 
1122
1270
  - 8a8870e: Add points-priced offers with declared stock, participant redemption hooks, automatic or staff-handover fulfillment, and operator cancellation and refund controls.
1123
- - 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 [upgrade guide](https://console.domino.run/docs/reference/upgrade-to-v1#explicit-resource-archive-and-restore).
1271
+ - 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](https://console.domino.run/docs/concepts/quests#resource-lifecycle).
1124
1272
  - 0a89168: Operators can archive point ledgers and correct points from Console member detail. `management.pointLedgers.list()` includes nullable `archivedAt`, and `management.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.
1125
1273
  - 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](/docs/reference/upgrade-to-v1#typed-trigger-policies).
1126
1274
 
@@ -1186,7 +1334,7 @@ No changes in this release.
1186
1334
 
1187
1335
  ### Minor Changes
1188
1336
 
1189
- - 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, and `domino webhooks` commands. This is additive; existing integrations need no changes. See [Outbound webhooks in the v1 upgrade guide](https://console.domino.run/docs/reference/upgrade-to-v1#outbound-webhooks) for an optional polling migration.
1337
+ - 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, and `domino webhooks` commands. This is additive; existing integrations need no changes. See [Receive outbound webhooks](https://console.domino.run/docs/build/webhooks) for an optional polling migration.
1190
1338
 
1191
1339
  ## 1.0.0-next.26
1192
1340
 
@@ -1455,7 +1603,7 @@ No changes in this release.
1455
1603
 
1456
1604
  - c320586: A prerequisite is the quest key alone. `completed(quest)` returned `{ quest, scope: "ever" }`, and `scope` was 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 carry `scope`. Releases stored with it still read, and the field is dropped. Code that reads `requires[].scope`, or builds `{ quest, scope: "ever" }` by hand, should use `{ quest }` or `completed(quest)`.
1457
1605
 
1458
- See [Prerequisites no longer carry a scope](/docs/reference/upgrade-to-v1#prerequisites-no-longer-carry-a-scope) in the upgrade guide.
1606
+ See [Prerequisites are typed eligibility rules](/docs/reference/upgrade-to-v1#prerequisites-are-typed-eligibility-rules) in the upgrade guide.
1459
1607
 
1460
1608
  ### Patch Changes
1461
1609
 
package/README.md CHANGED
@@ -12,12 +12,13 @@ pnpm add @domino-sdk/relay
12
12
  | `@domino-sdk/relay/browser` | The browser client with durable submission recovery, wallet signers and sign-in options |
13
13
  | `@domino-sdk/relay/react` | React hooks over the browser client |
14
14
  | `@domino-sdk/relay/avatar` | Generated default avatars (`avatarSrc`, `defaultAvatar`) |
15
- | `@domino-sdk/relay/authoring` | Quest, tracker, leaderboard, referral and engagement definitions, and evaluator types |
15
+ | `@domino-sdk/relay/authoring` | Quest, tracker, leaderboard, referral and engagement definitions, and evaluator types |
16
+ | `@domino-sdk/relay/server` | The server-side management client (`createManagementClient`, `hostedManagementClient`) |
16
17
  | `@domino-sdk/relay/schema` | Zod schemas of Relay's API bodies, deployments and tracker records |
17
18
  | `@domino-sdk/relay/build` | Bundling authored modules; needs `esbuild` installed |
18
19
  | `@domino-sdk/relay/portal-proxy` | Server proxies and hosted app Workers |
19
20
  | `@domino-sdk/relay/vite` | The Vite plugin that keeps server code out of the browser build |
20
- | `@domino-sdk/relay/testing` | Unit-testing quests locally (`runQuest`); for test files only |
21
+ | `@domino-sdk/relay/testing` | Unit-testing quests locally (`runQuest`); for test files only |
21
22
 
22
23
  `@domino-sdk/relay/internal` is shared by Relay's own services and Console. It is not a supported API and can change in any release.
23
24