# Publish changes and go live (/docs/build/publishing)



Every change to your quests, rewards and other definitions lands in **Test** first. When test looks right, someone prepares a promotion, and a person approves it in Console. Approval makes live match test in one step. Live never changes any other way, so nothing reaches live members without a person's approval.

* **In Console.** Choose **Promote to live**, then approve or reject the promotion under **Live → Deployments**, where you can also roll back to an earlier one. Quests and other resources can be archived in Console too.
* **Your website rides along.** If Domino hosts your app, the promotion also takes the build serving [staging](/docs/build/staging) live.
* **Code publishes to test.** Your developer publishes quests written in code to test with `domino deploy`. Quests made in Console are published through Quest Studio.

Hosting your website and publishing Domino behavior start as separate tracks. `domino stage` builds a pushed commit into a staging app on test data. `domino deploy` publishes quests and other resources to test. A promotion joins the tracks: it takes test's definitions and the build serving staging to live together.

```mermaid
flowchart LR
    subgraph Hosting["Website hosting (test data)"]
        Dev["domino dev preview"] --> Push["git push"] --> Stage["domino stage"]
    end
    subgraph Behavior["Domino behavior"]
        Preview["domino deploy --preview"] --> Test["Publish to test"]
        Test --> Promote["domino promote"] --> Approval["Approve in Console"]
    end
    Stage --> Promote
    Approval --> LiveApp["Live app and live definitions"]
```

| Command                                     | What it does                                                                                                 | Data         | After you stop working                                                      |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------ | --------------------------------------------------------------------------- |
| [`domino dev`](/docs/build/development)     | Serves the app on your device through an HTTPS preview URL and synchronizes authored behavior.               | Test         | Goes offline when development stops.                                        |
| `domino stage`                              | Builds the exact pushed commit into the staging app.                                                         | Test         | Stays online.                                                               |
| `domino deploy --preview`                   | Validates the deployment against test. Publishes nothing.                                                    | Test         | —                                                                           |
| `domino deploy --environment test`          | Publishes behavior to test.                                                                                  | Test         | —                                                                           |
| `domino deploy rollback --environment test` | Publishes the previous published test deployment again, undoing the latest one.                              | Test         | —                                                                           |
| `domino promote`                            | Prepares a promotion that makes live match test and carries the build serving staging, for Console approval. | Test to live | Remains pending until approved, rejected, or replaced by a newer promotion. |
| `domino deploy --environment live`          | Refused with 422 `promotion-required`. Code reaches live through a promotion.                                | Live         | —                                                                           |

## Publish behavior to test [#publish-behavior-to-test]

Your developer registers quest definitions, reusable types, collection slots, and other authored resources in `relay.json` and publishes them together. For ongoing content rewards, add `engagement: [{ entry: "engagement.ts" }]`. The engagement rule can reference a content quest and points balance in the same deployment. See [daily engagement rewards](/docs/build/trackers#settle-engagement-points-daily).

The project timezone decides where days, weeks and months begin for budgets, streaks and calendar leaderboards. See the [project calendar](/docs/concepts/calendar).

> [!DEV] Timezone and limits
> Set the project timezone with `"calendar": { "timeZone": "Europe/Budapest" }`. A deployment schedules a change only when this value differs from the last one deployed, so a timezone chosen later in Console stays until you edit relay.json; the preview's `calendar` shows the change it schedules, or `null`.
>
> One deployment holds up to 1,000 quests and 200 quest types; a release batch (`POST /releases/batch`) holds up to 1,000 releases, and a Console draft up to 1,000 quests and 1,000 collections. Engagement rules, trackers, point ledgers, referral programs and leaderboards stay at 50 each. Large deployments rely on the [shared SDK runtime](/docs/concepts/quests#shared-sdk-runtime): without it every quest carries its own copy of the SDK, and a deployment reaches the 64 MB request limit somewhere between about 180 and 400 quests, depending on what each imports.

### 1. Validate the remote deployment [#1-validate-the-remote-deployment]

```sh
pnpm exec domino deploy --preview --environment test --json
```

> [!WARNING] Console values win
> Console custom values take precedence over source defaults. A removed or incompatible custom setting can reject the entire batch. Resolve it in Console or preserve an intentional rename with `settingRenames`.

The preview says what publishing does to each quest, in `questChanges`, by key:

| Change      | Means                                                                                                                                                             |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `created`   | The quest is new.                                                                                                                                                 |
| `updated`   | A new release replaces the quest's current one; `replaces` names that release and when it was created.                                                            |
| `unchanged` | The current release already has the same code, SDK runtime, provider, settings and archive state. Publishing keeps it: no new release, and its ID stays the same. |

Check the `updated` quests before you publish. A quest you did not mean to change shows up here, for example when your checkout holds older code for a quest someone else deployed since. Unchanged compares what would be published, not your source files: a rebuild that produces the same output is unchanged.

Missing project configuration does not reject the deployment. When a quest needs an integration the project hasn't connected, such as Discord, the preview and the published result list it in `warnings`, and the CLI prints it:

```text
Warning: 2 quests need Discord, but no Discord server is connected to this project and environment. Publication continues; until Discord is connected in Console Integrations, attempts fail with integration-required. Connecting it admits the failed attempts of automatic quests without a tracker again. Quests: join-server, earn-role.
```

Each warning has a `code`, the affected `quests` by key, a `message`, and, for an integration, the `integration`. Treat an unknown code as a notice its message explains. A broken quest definition still rejects the deployment. Archived quests are not listed.

| Code                     | Means                                                                                                                                                                                                                                                                   | What to do                                                                                                                                                                      |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `integration-required`   | The quests need an integration the project hasn't connected.                                                                                                                                                                                                            | Connect it before members attempt those quests; see [recover a configuration failure](/docs/build/integrations#recover-a-configuration-failure).                                |
| `x-post-rewards-ungated` | The quests reward X posts from anyone who posts: an X tracker trigger that pays the post's author, or a content quest on an X tracker, with declared rewards or an engagement rule, and no `inGroup` prerequisite. X does not allow apps that reward users for posting. | Add `inGroup(group)` with a group of creators you approve, and manage its members in Console **Groups**. See [reward X posts only for approved creators](/docs/build/trackers). |
| `quests-not-in-bundle`   | The quests are published in test from code but this deployment leaves them out, usually because their files were deleted, such as a starter's sample quests. They stay as published. Quests created in Console and `--only` deployments are not listed.                 | [Archive](#archive-a-quest) each quest you no longer want.                                                                                                                      |

### 2. Publish to test [#2-publish-to-test]

```sh
pnpm exec domino deploy --environment test --json
```

In a terminal, `domino deploy` validates the server preview, then shows the organization, project, environment, API URL, deployment ID, resource counts, and each created or updated quest by key (`station-race: updated`), with a count of the unchanged ones, before asking to publish. Press Enter to cancel, or type `yes` to publish. Cancellation leaves the validated preview unpublished.

Engagement entries also show whether each rule is new, unchanged, or a formula change, with the retained accounting summary for changes. Formula changes preserve past earnings and budgets. A stale engagement checkpoint rejects the entire deployment; create a fresh preview. Recover a lost publication response with `domino deploy --resume DEPLOYMENT_ID`.

> [!DEV] When the publication response is lost
> The CLI sends the same publication again, at most three times in all within 30 seconds; Domino publishes a preview once and answers a repeat with that result. If no answer arrives, the command exits 3 with the error code `OUTCOME_UNCERTAIN` and prints the `domino deploy --resume <id>` command that finishes it; with `--json`, the error's `operation.resume` holds its arguments, including the scope flags a resume requires. Resuming publishes that preview once or returns its committed result. Starting another deployment can create another immutable release. See [Resume an uncertain change](/docs/reference/automation#resume-an-uncertain-change).
>
> Losing the answer while the CLI prepares the preview publishes nothing, so there is nothing to resume: the command exits 1 with `TIMEOUT` or `NETWORK_ERROR`, and running `domino deploy` again is safe.

### When the CLI asks for confirmation [#when-the-cli-asks-for-confirmation]

| Invocation                                                      | Confirmation                               |
| --------------------------------------------------------------- | ------------------------------------------ |
| `domino deploy` in a terminal                                   | Asks before publishing.                    |
| `domino deploy --resume <id>`                                   | Also asks before publication.              |
| `--preview` or `--dry-run`                                      | Does not ask, because they do not publish. |
| `--yes`                                                         | Skips the terminal confirmation.           |
| `--json`, `--no-interactive`, CI, or redirected input or stderr | Preserves noninteractive execution.        |

With `--promote`, the confirmation prepares a promotion for review. It does not publish. `--yes` skips only that terminal confirmation; a person still approves the promotion in Console.

### Publish only some quests [#publish-only-some-quests]

When several people or agents work on one project in parallel checkouts, each full deployment republishes every quest from its own checkout and can overwrite a quest someone else just changed. Publish only the quests you worked on, by key:

```sh
pnpm exec domino deploy --only daily-check-in --only station-race --environment test
```

Repeat `--only` once per quest. Every other quest keeps its current release, and every other resource in `relay.json` (quest types, collections, point ledgers, engagement rules, leaderboards, the calendar and the rest) stays as published. The exception is `supportedInteractions`: the deployment sets it from your `relay.json`, so a named quest can use an interaction you just added. Keep your checkout's list current, or an older list replaces a newer one. Rolling back a partial deployment republishes the full input of the deployment before it, so every quest it holds, including the ones the partial deployment changed, goes back to its version there; quests only the partial deployment added stay. In a terminal, the confirmation says it is a partial deployment and lists the quests.

> [!DEV] What a partial deployment holds
> The bundle holds the named quests, the SDK runtime they import, the reference images whose key their code names as a string literal, and `relay.json`'s `supportedInteractions`. Domino checks the named quests against what test already has, so deploy the whole project when they need a resource that isn't published yet. `--only` matches the `key` a quest file passes to `defineQuest`; a key computed in code can't be selected. An unknown key fails with `INVALID_ARGUMENT` (exit 2), naming the discovered keys, before anything is sent. `--only` works with `--preview` and `--yes`, and is refused with `--resume`, `--promote`, `--dry-run` and `--out`, which act on a whole stored deployment or bundle.

### Roll back a test deployment [#roll-back-a-test-deployment]

To undo the latest deployment to test, publish the one before it again.

* **In Console.** In test, open **Deployments**. **Code deployments** lists what `domino deploy` published, newest first, with the one test runs marked **Current**. Choose **Roll back to** with the previous deployment's time, check what the confirmation says changes, then choose **Roll back**. The button is unavailable, with the reason beside it, when nothing was published before the current deployment. People without publish permission don't see it.
* **From the CLI.** Run `pnpm exec domino deploy rollback --environment test`. It names both deployments and asks before publishing, like `domino deploy`.

The rollback publishes at once, as a new deployment, and appears in the list as **Rollback to** that time. Only the previous published deployment can be rolled back to, so rolling back again undoes the rollback.

> [!WARNING] What a rollback leaves alone
> Only what the earlier deployment contains goes back. Quests and other definitions that only the later deployment added stay; [archive them](#archive-a-quest) if they should go. The app build serving [staging](/docs/build/staging) does not change either: stage the earlier commit again for that. A staged build publishes its own definitions, so once a hosted app build has published to test since the earlier deployment, the rollback is refused with 409 `conflict`; stage the earlier version of the app instead.

This rolls back test only. To take the rollback live, [promote](#publish-behavior-to-live) afterwards; to undo a change in live directly, [roll back a promotion](#roll-back-a-promotion).

> [!DEV] Roll back from code or a script
> `domino deploy rollback [DEPLOYMENT]` rolls back to the previous published deployment by default; the API refuses any other ID with `validation-failed`. `--yes` skips the confirmation. The CLI saves the change before sending it, so after an uncertain response (exit 3) run the error's `operations resume` command, which finishes it once. With `--json` the result has the same shape as `domino deploy`'s: `deployment` is the new deployment's ID and `status` is `published`, with `rolledBackTo` naming the earlier one. In live it fails with `ENVIRONMENT_UNSUPPORTED` and `apiCode` `promotion-required`.
>
> `management.deployments.rollback(DEPLOYMENT_ID)` with a test client does the same, with publish permission; its record has `source: { kind: "rollback", deployment }`. `management.deployments.history()` names the target as the second entry of `published`.

## Stage a hosted app [#stage-a-hosted-app]

`domino stage` builds a pushed commit into the project's persistent staging app on test data. It is separate from publishing behavior: see [Stage and share](/docs/build/staging). To put the staged build live, promote; set live's [app secrets](/docs/build/hosted-app#set-app-secrets) first.

## Publish behavior to live [#publish-behavior-to-live]

Live changes only when a person approves it. The usual path is a [promotion](/docs/concepts/promotion): change and try definitions in test, then promote the whole environment.

* **Who prepares.** Anyone with publish permission in test, from Console, the CLI or the management client.
* **Who approves.** Only a person signed in to Console with publish permission can approve or reject it. API keys, service keys, hosted-app keys and CLI sessions cannot approve.

Live refuses a direct change to a quest, its settings, a point ledger, a badge, the calendar or another definition with 422 `promotion-required`, from Console as well as from API keys. It refuses a direct code deployment the same way. See [test and live](/docs/concepts/environments).

### 1. Prepare a promotion [#1-prepare-a-promotion]

In Console, choose **Promote to live** in the project header beside the environment switch, which shows while you're in test, or on the notice that every definition editor shows in live. Console prepares the promotion from test and opens it in **Live → Deployments**, ready to approve. If a promotion is already awaiting approval, Console asks first: **Open it** shows the waiting one, and **Replace it** prepares a new one, which supersedes it. People without publish permission don't see the action.

When live already matches test, Console says **Live already matches test** and saves nothing.

Your developer can prepare the same promotion from the CLI:

```sh
pnpm exec domino promote --environment test --json
```

> [!DEV] Prepare from code
> From code, call `management.promotions.prepare()` with a client for the test environment. The result is a pending promotion:
>
> | Field | What to check |
> | --- | --- |
> | `changes` | Each definition live would change, by `kind` and authoring `key`, with `added`, `changed`, `archived` or `unarchived`. |
> | `warnings` | `binding-missing`: an integration, webhook endpoint, sign-in method or app secret test uses and live lacks. Set it up in live; a promotion never copies it. `live-only`: a definition only live has, left unchanged. `group-created`: a group, by `key`, that approval creates in live with no members. |
> | `build` | The hosted app build serving staging, which the promotion carries, or `null`. |
>
> When live already matches test, preparing fails with `promotion-empty` and nothing is saved. Preparing again replaces any older pending promotion, which becomes `superseded`.
>
> The CLI saves the request before sending it. If the response is lost, run the `operations resume` command from the error; the same operation ID answers with the same promotion.

### 2. Approve it in Console [#2-approve-it-in-console]

Select the project and **Live**, then open **Deployments** under Developers. **Awaiting approval** shows the pending promotion:

* **Changes in live**, grouped as **Added**, **Changed**, **Unarchived** and **Archived**, each with its kind, title and authoring key.
* **Groups approval creates**: groups the definitions name and live lacks, created empty.
* **Left as they are**: the other warnings. A missing binding has a button that opens where live sets it up: **Open Integrations**, **Open Webhooks**, **Open Sign-in** or **Open App secrets**.
* **App build**: the hosted app build it carries, or **None**.

Choose **Approve and apply**, check the confirmation that counts the definition changes and names the build it takes live, and choose **Approve and apply** again to apply every change in one transaction; or **Reject**, enter a **Rejection reason**, and choose **Reject promotion**. Without publish permission, the promotion shows without these actions.

When the promotion carries a build live does not serve yet, Domino then deploys it as the live app at `<project-slug>.domino.page`, typically within a couple of minutes; the previous live build serves until it switches. If that deployment fails, the previous build keeps serving, and approving the next promotion retries it. See [the hosted app](/docs/concepts/promotion#the-hosted-app).

If test or live changed after the promotion was prepared, approval fails with `promotion-changed` and live is unchanged. Console explains this and offers **Prepare again**, which prepares a new promotion to review.

> [!IMPORTANT] Groups are never copied
> When a quest, offer, leaderboard or referral program names a group live lacks, such as one a quest rewards with [group membership](/docs/concepts/groups#add-members-with-a-quest-reward), approval creates it in live with no members. Members are never copied from test. A group archived in live is never unarchived: if a quest rewards membership in it, or a leaderboard binds it as its exclusion group, approval refuses with `409`, naming the quest and the group, until you unarchive the group in live.

**Promotion history**, below, lists applied, rejected and superseded promotions, newest first; the one live matches is marked **Live now**. `domino promotions list` and `domino promotions get PROMOTION_ID`, or `management.promotions.history()` and `get(id)`, read promotions from either environment.

### Roll back a promotion [#roll-back-a-promotion]

In **Live → Deployments**, choose **Prepare rollback** on an earlier applied promotion in **Promotion history**. The one marked **Live now** has none, because live already matches it.

The rollback appears under **Awaiting approval** as **Rollback to the promotion approved** with that promotion's approval time; review and approve it like any other. It adds to the history. Changes made in test since then are carried again by the next promotion from test.

A rollback is a new promotion of the definitions and build that the earlier promotion made live. Every live definition that promotion did not carry is archived in it, and pickup policies it did not have go back to untracked.

> [!DEV]
> From code, `management.promotions.rollback(PROMOTION_ID)`, with a client for live, prepares the same rollback.

### Code deployments reach live through a promotion [#code-deployments-reach-live-through-a-promotion]

Live refuses a code bundle sent to it directly. `domino deploy --environment live`, with or without `--preview`, and `management.deployments.preview` with a client for live fail with 422 before the bundle is checked, whatever the credential:

```json
{
  "error": "Code changes in test and reaches live through a promotion. Deploy to test, then run `domino promote`.",
  "code": "promotion-required"
}
```

The CLI reports it as `ENVIRONMENT_UNSUPPORTED` (exit 1) with `apiCode: "promotion-required"`. Deploy the bundle to test, then [prepare a promotion](#1-prepare-a-promotion).

`domino deploy --promote TEST_DEPLOYMENT_ID --environment live` and `management.deployments.promote(id)` are deprecated and removed in 2.0.0: use [`domino promote`](#1-prepare-a-promotion) and `management.promotions.prepare()` with a client for test, which answer the promotion itself. Until then they check that the test deployment was published, then prepare a promotion of all of test. They publish nothing, and answer in the deployment record shape: `status: "pending"`, no resources, and a first warning with code `environment-promotion` that names the promotion. Its `id` is the promotion's: read it with `management.promotions.get(id)` and approve or reject it as a promotion; `deployments.get`, `deployments.publish`, `deployments.submit` and `deployments.reject` do not accept it. Like `domino promote`, the command runs in the checkout's project and needs no scope flags.

`management.deployments.submit(id)` and `management.deployments.reject(id, { reason })` are deprecated too: they act only on live deployments made before live refused them.

Console's **Live → Deployments** shows promotions only. `management.deployments.history({ cursor, limit })` still pages live deployments submitted before live refused them, newest first, and names the two latest published deployments beside every page.

### Work already under way keeps its rules [#work-already-under-way-keeps-its-rules]

Publishing new rules does not change attempts members already started: they finish under the rules they were admitted with, and completions are not reset. Pausing a quest stops new attempts only. See [republishing and automatic quests](/docs/reference/quest-rules#republishing-and-automatic-quests).

## Archive a quest [#archive-a-quest]

Archiving stops new participation in a quest and hides it from your app, while members keep what they earned. Deleting a quest's source file stops deploying it, but the quest stays published, so archive it too.

Operators archive a quest from its page in Console, under **Participation**. From the CLI:

```sh
pnpm exec domino quests archive show-your-setup --reason "Replaced by the new campaign" --environment test --json
```

The argument is the quest's key or ID.

| Effect                        | Details                                                                                                                                                                                                                                                         |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No new attempts               | Submissions, staff confirmations, and tracker triggers stop admitting the quest. Attempts already admitted keep their release; content qualification and engagement settlement for the quest wait, with the reason "Quest is archived", until it is unarchived. |
| Hidden from collections       | Your app's collections stop listing it. Completions, attempts, points, and rewards stay.                                                                                                                                                                        |
| Not counted as an interaction | An archived quest no longer needs your app to support its interaction, so a deployment can drop that interaction from `supportedInteractions`.                                                                                                                  |
| Stays archived                | Deploying the quest's code again publishes the new rules as archived.                                                                                                                                                                                           |
| Requirements keep working     | Quests that require it stay open to members who completed it. Others can no longer unlock them.                                                                                                                                                                 |

Run `domino quests unarchive <key> --reason "…"` to reopen it. Unarchiving fails when your app no longer supports the quest's interaction or the quest no longer fits the prerequisite rules, such as the shared streak timezone. A quest with an empty resource setting reopens paused. Activity from while it was archived is not replayed.

> [!NOTE] Archiving in live
> Archive state is a definition, so live refuses archive and unarchive with `promotion-required`, from Console too. To stop new participation in live, pause the quest with `management.quests.pause` or **Pause quest** in Console. Quests created in Console change participation in their Console editor.

## Keep resource identity stable [#keep-resource-identity-stable]

Each quest, reward or leaderboard in code has a `key` that Domino uses to recognize it across deployments. Keep it unchanged and members keep their progress; change it and Domino treats the result as a new resource.

Use `key` for authored quest, quest type, leaderboard, referral program, reward, and collection declarations. Keep each key unchanged when renaming a title or moving a source file. Domino assigns the stored ID and reuses it on later deployments. Existing stored IDs and references remain valid.

Define point ledgers with `defineLedger({ key, name, description? })`, then list each module export under `pointLedgers` in `relay.json`. You can also create ledgers in Console for operators to select through `settings.pointLedger`.

* **References.** Rewards can reference a declared key or an existing ledger ID. Unknown references fail publication.
* **Timing.** Previewing a bundle does not make a new ledger available for awards; publishing commits the ledger with the rest of the bundle.

The CLI uses the project ID returned by project creation. Project names and hosted URL slugs are separate from that identity.

## Upgrade existing declarations [#upgrade-existing-declarations]

Authoring constructors use `key`; the legacy `id` alias is removed. Keep the old value when renaming the property so redeployments retain identity. This source key is not the opaque ID Domino stores.

### 1. Review the migration [#1-review-the-migration]

Run `domino migrate` to review an offline diff. It renames `id` to `key` and `balance` to `ledger`, migrates collection declarations, and proposes missing point ledger declarations for static references. A `HANDLE_REFERENCE_REQUIRED` error marks a string that needs a handle import; make that change by hand.

### 2. Apply it [#2-apply-it]

Run `domino migrate --write` to apply the diff. Review the ledger names before publication.

### 3. Check the result [#3-check-the-result]

Run `domino check --json` afterward. Source diagnostics include file locations, stable codes, and suggested fixes.

* **Warnings** allow existing code to keep working.
* **Conflicting identity values** fail the check.
* **Dynamic declarations** remain unchanged and receive manual instructions.

The migration is safe to rerun and never renames stored server IDs.

### Evaluator protocol [#evaluator-protocol]

Each quest bundle declares the evaluator protocol it was built for, and Relay refuses an unsupported version before evaluation; rebuild with a supported SDK. See [evaluator protocol](/docs/reference/verification#evaluator-protocol) and [the v1 upgrade guide](/docs/reference/upgrade-to-v1#evaluator-protocol).

## Archive and restore resources [#archive-and-restore-resources]

Archiving a tracker, leaderboard, referral program or other resource stops it without deleting its data or history. Restoring brings it back.

Open **Settings → Resources** in Console, select the project and environment, and find the resource by name, key or kind. Choose its archive or unarchive action, such as **Archive tracker**, read the effect, and provide a reason. Trackers, leaderboards and referral programs offer the same action on their own pages: a tracker on its page, a leaderboard under **About this board**, and a referral program on its card. Read permission allows inspection; publish permission allows changes. Live refuses archive and restore with `promotion-required`, from Console too, so make these changes in test.

For the test environment, use the CLI:

```sh
domino resources list --environment test --json
domino trackers archive events --reason "Source retired" --environment test
domino trackers unarchive events --reason "Source returned" --environment test
```

The kinds are `tracker`, `referral`, `leaderboard`, `engagement`, `collection`, `quest-type`, `image`, and `point-ledger`. The reference accepts a key or stored ID. For a quest instance, use `domino quests archive|unarchive <key> --reason <text>` or its Console page instead.

Each change keeps its resource's ID, data and history. See [resource lifecycle](/docs/concepts/quests#resource-lifecycle) for the effect on each kind. Restoring a collection also restores its selected quests. Restoring an engagement rule can pay growth since its retained accounting watermark.

Deleting source files, promoting another environment's code, or republishing older code does not change the destination's archive state. An archive or restore invalidates a prepared catalog preview; preview again to publish against the state you reviewed.

> [!DEV] Retries and the management client
> The `resources archive|unarchive` compatibility commands save the operation before sending it. The resource-specific commands use `--action-id <uuid>` for identical retries. For the compatibility commands, reuse `--operation-id <uuid>` with identical input or run `domino operations resume <uuid>` after an interruption.
>
> The management client exposes `management.resources.list()` and `management.trackers.archive(reference, { actionId, reason, expectedArchivedAt })` and `management.trackers.unarchive(reference, { actionId, reason, expectedArchivedAt })`. `expectedArchivedAt` is optional; supply the timestamp or `null` from the list to refuse a change after another operator changed that resource.
