[Migration map](../reference/tool-name-migration.md).
# signup_form_apply — save sign-up form requirements
Change enabled/required settings and custom-question names together, preserving other form settings and verifying the complete saved form. Read [signup_form_get](publ_signup_form_get.md) first. This operation edits collection settings; it does not create accounts, submit answers, delete past answers, or prove members have supplied information again.
## Input
| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `requestId` | string | Yes | Stable ID matching `^[A-Za-z0-9_-]{8,80}$`. Keep the original ID and input for recovery. |
| `channelCode` | string | Yes | Selected channel; nonempty and neither `.` nor `..`. |
| `expectedRevision` | string | Yes | The 64-character lowercase hexadecimal revision returned by get. |
| `changes` | array | Yes | 1–44 distinct editable field changes. Each selects a returned `key` and supplies at least one of `enabled`, `required`, or `name`. |
| `dryRun` | boolean | No | Defaults to `false`. `true` prepares a plan without writing form settings. |
Each change is strict: `key` is a nonempty string; `enabled` and `required` are optional booleans; `name` is an optional string trimmed before use, at most 100 characters. Unknown properties are rejected at both levels. Null is not a clearing value. Additional runtime rules below are not fully represented in the exported JSON schema.
```json
{"requestId":"signup-form-example-001","channelCode":"selected-channel-code","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","changes":[{"key":"profile_add_info_contact_address","required":true}],"dryRun":true}
```
Replace the channel and revision placeholders with a fresh get result. This illustrative plan requests mandatory address collection and therefore enables the address field as well. It is not a recorded live call or a usable zero revision.
## Change rules
| Change | Result |
| --- | --- |
| `required:true` | Also enables the field. |
| `enabled:false` | Also clears required. |
| `enabled:false` together with `required:true` | Rejected as contradictory input. |
| `required:false` alone | Clears required while preserving enabled state. |
| `enabled:true` alone | Enables while preserving required state; activating a custom slot also validates its name. |
| `name` | Only for one of the five CUSTOM keys; fixed questions and essential fields cannot be renamed. |
| Omitted property/key | Preserve its current effective value. |
Email, nickname and country are locked even if the proposed value matches. Duplicate keys, unknown keys, changes without an actual property, and incomplete nonempty forms are rejected. Supply keys from get; do not invent a sixth custom slot or use a field's translated label.
A supplied custom name is trimmed. When naming a slot or activating a previously disabled slot, the resulting name must be 2–100 characters if active. An inactive slot can have an empty name, but a nonempty supplied inactive name still needs 2–100 characters. Existing unchanged names are retained; the workflow does not globally repair legacy names. Disabling without a name preserves its existing name. Check the name before asking to enable a blank custom slot.
Enabling questions may also request information from existing members. Renaming is not evidence that their answers were collected again. Disabling a question does not mean deleting prior answers.
## Planning and write sequence
The connection, CHANNEL_MANAGEMENT origin, selected-channel credential and publication permission requirements are the same as [get](publ_signup_form_get.md).
For a new request, the workflow reads the complete form, compares its revision, merges the changes and checks field rules. A nonempty partial response blocks application with `SIGNUP_FORM_INCOMPLETE`. Only an exactly empty array receives the explicit initial defaults.
A real request stores the original input, its fingerprint, connection/channel identity, before/target snapshots and phase. Before writing it reads again, requires the original before revision, and revalidates the plan. If the current form already equals this request's before and target values, it skips PUT. Otherwise it records `sent`, sends one PUT, and reads the entire form back before completing. There is no credential-refresh, member-update, separate publish or rollback step in this workflow.
PUT `/seller/api/v1/profile-additional-information-fields` contains `{profileAdditionalInformationFields:[...]}` with all 44 editable fields. Each row has `key`, `isActivated`, and `isRequired`; only CUSTOM rows contain `customFieldName`. The three essential records, IDs, and types are omitted. The bridge builds this full payload; callers supply only requested changes.
The before/readback comparisons are not atomic server preconditions. Another editor can change the form between reads and PUT. Readback equality confirms observed effective settings, not who wrote them or every later state.
## Inputs
| Field | Required here | Contract |
| --- | --- | --- |
| `changes` | Yes | array; min items 1; max items 44 |
| `changes[].key` | Yes | string; min length 1 |
| `changes[].enabled` | No | boolean |
| `changes[].required` | No | boolean |
| `changes[].name` | No | string; max length 100 |
| `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 dry runs
| Result | Business fields and meaning |
| --- | --- |
| New-request `PLAN_READY` | `taskType`, environment, channel, planned `fields`, `impact`, `writeNeeded`, and `writesPerformed:false`. No `taskId` or durable job is created. |
| Existing-job `PLAN_READY` | `taskId`, `taskType`, saved `phase`, target `fields`, and `writesPerformed:false`. Context and fingerprint are checked, but no current form read is made; environment/channel/impact/writeNeeded are not present in this payload. |
| `SIGNUP_FORM_APPLIED` | `taskId`, `taskType:signup-form`, environment, channel, `phase:done`, resulting `fields`, `revision`, `impact`, and `verification:{confirmed:true,method:form_settings_read,memberAnswersRead:false,memberRecollectionVerified:false}`. |
There is no `changed` flag in the completion payload. Completion can be a no-op or a stored prior completion. Existing-job matching ignores only `dryRun`: retain the original revision, change order, values and target when using the same ID. After a new dry run, submit the same request with `dryRun:false` to execute; freshness is checked again. An ID found only in an error or dry-run plan is not proof a durable job exists.
Successful get, new-request plan and completion results can resolve the one sign-up-form console URL described by get. An existing-job dry-run result lacks channel context and has unavailable navigation; task status also has unavailable navigation and removes stored nested navigation. Do not substitute a home URL.
## Recovery and failures
Use [task_status](publ_task_status.md) and [task_resume](publ_task_resume.md) with the original `taskId` and explicit `taskType:signup-form`.
| Saved phase | Behavior |
| --- | --- |
| `prepared` | Resume may write after a fresh before-revision comparison and plan validation. A changed before state is a conflict, even if another actor reached the target. |
| `sent` | Resume reads and compares with the saved target. Matching values complete the job; differing values return `SIGNUP_FORM_OUTCOME_UNKNOWN`. It does not resend the uncertain PUT. |
| `done` | Apply/resume checks connection/channel context and returns the saved completion without a fresh form read. Use get for current state. |
Status reads saved phase and `lastResult`, with a connection identity check. It returns `pending` until done and does not execute or freshly read the form. Known non-unknown `BridgeError` failures from PUT return the phase to `prepared`; an unknown write remains `sent`. A post-write GET failure can leave `sent` even if the reported error is a rejected read. Inspect phase and outcome together instead of assuming no change occurred.
Other important codes are `SIGNUP_FORM_CONFLICT`, `TASK_CONTEXT_CHANGED`, `SIGNUP_FIELD_UNKNOWN`, `SIGNUP_FIELD_DUPLICATE`, `SIGNUP_ESSENTIAL_LOCKED`, `SIGNUP_NAME_LOCKED`, and `SIGNUP_QUESTION_NAME_INVALID`. Unknown outcomes require observation or human inspection of the original job; a new request ID is not a recovery shortcut. See [common results](../reference/results.md) for wrapper, authentication and error boundaries.
## Saved plan replay
An existing task's `dryRun` returns historical saved plan/progress, with
`historical:true`, `replayed:true` and false current verification. It does not
freshly read the business state, repeat effects or change the journal, and it has
no current console link. Follow `nextStep` for a current observation; keep the
original task ID for uncertain-outcome status/resume. A new task still follows
normal fresh-plan validation. Do not create another ID to bypass uncertainty.
nextStep reads signup_form_get for the original channel.
## Saved record identity
Existing apply/dry-run calls and task status/resume require both the saved task ID
and saved input requestId to match the requested ID. A mismatching or malformed
saved identity returns `TASK_STATE_UNVERIFIED` before workflow context or business
reads. Preserve the original record for inspection; do not rename/edit it, try
its embedded ID or create a replacement request to bypass the error. No record
is repaired or resent. Matching records retain their existing recovery and
freshness checks; this rule does not convert them into universally read-only calls.