questionnaires_visibility_apply
[Migration map](../reference/tool-name-migration.md).
# questionnaires_visibility_apply
This implementation is a dependency of the documentation branch.
Set one desired active state for a bounded selection of questionnaires. This is a stored-state change; it does not prove visitor access or submission eligibility.
## Access and input
Requires OWNER or DELEGATEE with MANAGE_MARKETING_TOOLS on the selected channel. The source has no questionnaire plan or connected-domain gate. Hosted selected-channel resources admit these tools only for the exact channelCode; account resources exclude them.
channelCode and questionnaire/answer IDs are strings of 1–512 characters, excluding literal dot/double dot, slash, backslash, ASCII controls and DEL. Obtain them from authorized discovery/results; numeric row IDs, URLs and credentials are not substitutes. Strict input objects reject unknown properties.
| Input | Contract |
| --- | --- |
| channelCode | Required selected channel. |
| requestId | Required 8–80 ASCII letters, digits, underscores or hyphens. |
| targets | Required array of 1–100 unique selected questionnaires. |
| targets[].qrid | Required returned string editor ID. |
| targets[].expectedRevision | Required 64 lowercase hex characters from that record's latest detail revision. |
| active | Required boolean; true activates, false deactivates. |
| dryRun | Optional boolean, default false; true plans with no business effect or task JSON. |
Read every target with [questionnaires_get](publ_questionnaires_get.md) detail. A creationRevision or a list's numeric row ID is not a substitute. Replace the illustrative IDs and zero digest below with current values. Unique targets are a runtime refinement; the JSON export alone does not express that condition.
```json
{
"channelCode": "selected-channel-code",
"requestId": "questionnaire-active-01",
"targets": [
{
"qrid": "selected-questionnaire-id",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000"
}
],
"active": true,
"dryRun": true
}
```
```json
{
"channelCode": "selected-channel-code",
"requestId": "questionnaire-inactive-01",
"targets": [
{
"qrid": "selected-questionnaire-id",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000"
}
],
"active": false,
"dryRun": true
}
```
## State mapping and batch behavior
active:true sends the activate-questionnaires POST and verifies stored isDeactivated:false. active:false sends deactivate-questionnaires and verifies true. The endpoint receives only qrids whose prior state differs. It accepts only HTTP200/204 with no response body. Other statuses are not implicitly accepted; this acknowledgement rule is distinct from create200/201 and revise200. Already matching selections remain part of full readback but are omitted from the POST. An entirely matching batch completes as a no-op without a POST. There is no plan/domain gate, image upload, recache or deletion.
Before preparing and before the effect, repeated reads compare all selected full records and selected channel/operator/credential context. Each expectedRevision must match. Each returned/stored target must have a positive channelId equal to the selected channel. After the effect all targets, including unchanged ones, must match desired activation plus all preserved business fields, including supported null values. The derived answersCount is excluded. Expected root updatedAt/lastRevisedAt and question updatedAt may change across save, but remain in snapshots, revisions and pre-save conflict checks. Questions compare after explicit-order normalization; option positions remain significant. No other unknown business field is ignored. This is not an atomic multi-record transaction or server conditional write. A partial batch stays pending; it is never automatically retried with a smaller selection.
## Inputs
| Field | Required here | Contract |
| --- | --- | --- |
| `qrids` | Yes | array; items: string; min length 1; max length 100; min items 1; max items 100; Questionnaire qrids from questionnaires_get. |
| `active` | Yes | boolean; true turns them on, false turns them off. |
| `confirmToken` | No | string; pattern `^ct[a-f\d]{32}$`; The confirmToken from this tool's preview result. Omit it to preview; repeat the same input with it to execute. |
## Results and recovery
The workflow shares [questionnaire result fields](publ_questionnaire_apply.md#results-and-full-readback) and taskType questionnaire. Dry run returns QUESTIONNAIRE_PLAN; fresh completed state returns QUESTIONNAIRE_COMPLETE and per-target revisions. noop, saveAcknowledged and writesPerformed distinguish an unchanged selection, acknowledged POST and uncertain attempt. Stored readback does not prove causality, visitor behavior or atomicity.
Use the original taskId/input with status/resume. prepared can continue after guards; save-sent observes every original target without resending activation; done is historical with confirmed:false. REQUEST_ID_CONFLICT rejects changed inputs under the same ID. Stale revisions, changed context and malformed journals do not permit a forced retry. No automatic rollback is provided. Refer to the [phase table](publ_questionnaire_apply.md#task-recovery) before recovering an uncertain batch.
The single console destination is the selected channel's questionnaire list, `/channels/{channelCode}/marketing-tools/questionaires`. Demo omits the live URL. The normal API service is host-configured; no credentials or service origins are tool inputs.