# Survey lifecycle (https://survey.dimah.dev/docs/surveys)



Each survey stores three independent concerns:

* `draftJson` — the editable SurveyJS document
* `publishedJson` — the document new responses may start
* `settings` — collection policy owned by dimah-survey

Creator writes the draft. Publishing explicitly promotes that draft. Starting
a response copies the published document into `response.definition`.

<Callout>
  `saveSurvey` only replaces `draftJson`. It never publishes, changes collection
  settings, or updates an existing response.
</Callout>

## Status and availability [#status-and-availability]

| Status     | Meaning                                           | Can start a response?         |
| ---------- | ------------------------------------------------- | ----------------------------- |
| `draft`    | No published document exists                      | No; `NOT_PUBLISHED`           |
| `active`   | `publishedJson` is available to fill              | Yes, while collection is open |
| `archived` | Collection is stopped; published JSON is retained | No; `NOT_PUBLISHED`           |

`GET /survey/published` on the fill handler returns `publishedJson` and
`settings` for an active survey. It never exposes `draftJson`. The read remains
available outside the collection window so the application can render a closed
state; start and writes fail with `SURVEY_CLOSED`.

## Authoring operations [#authoring-operations]

| Operation            | Effect                                                                      |
| -------------------- | --------------------------------------------------------------------------- |
| `saveSurvey`         | Create the row or replace `draftJson`; insert default settings once         |
| `publishSurvey`      | Copy `draftJson` to `publishedJson`, set `active`, and update `publishedAt` |
| `archiveSurvey`      | Set `archived` without deleting either document                             |
| `resumeSurvey`       | Return an archived, previously published survey to `active`                 |
| `saveSurveySettings` | Replace the complete `settings` object without touching either document     |

`resumeSurvey` does not copy `draftJson`. An already active survey is returned
unchanged. An archived survey with no published document throws
`NOT_PUBLISHED`, and resume never runs publish hooks.

Publish runs `onPublish` before the write and `afterPublish` after it.
`onPublish` may abort; an `afterPublish` failure leaves the published row in
place. See [Configuration](https://survey.dimah.dev/docs/configuration.md).

## IDs and slugs [#ids-and-slugs]

The application chooses `id`. On create, `slug` defaults to that id and must be
unique; a conflicting write throws `SLUG_TAKEN`.

`getSurvey` and `getPublishedSurvey` accept an id or a slug in the `id` query.
`startResponse` accepts either value in `surveyId`.

## Protect concurrent changes [#protect-concurrent-changes]

Draft, publish, archive, settings, and resume accept `expectedUpdatedAt` from
the last read. The store compares it inside the write and rejects a stale
request with `STALE_UPDATE`.

Omit the token only when a last-write-wins update is intentional. Creating a
survey with `expectedUpdatedAt` set fails with `STALE_UPDATE`, because there is
no row to compare.

`useSurveyDraft` sends the token it last read. See [Creator](https://survey.dimah.dev/docs/creator.md).

## Publish changes the future, not history [#publish-changes-the-future-not-history]

Existing drafts, submitted responses, and abandoned responses keep their stored
`definition`. Only a newly created response copies the latest
`publishedJson`.

There is no separate survey-version table. The snapshot on each response is
the version history that matters.
