# Edit an existing membership's presentation
Use [memberships_get](../tools/publ_memberships_get.md) to inspect an existing membership, then [membership_update](../tools/publ_membership_update.md) for its title, benefit statements and card styling. Original-byte card artwork can join the same existing-membership save; creation, product pricing and payments remain separate operations.
There is no type selector in the input or type field in the source PUT. New membership creation remains GENERAL. Actual system-type editing/restoration remains unverified; the earlier GENERAL observations retain their original scope.
## Select the existing membership
Select the authorized channel with channels_list and channel_context_get. An OWNER or a DELEGATEE with MANAGE_MONETIZATION can use this workflow. Hosted account scope excludes it; a selected-channel resource must match the channel exactly. Do not ask for credentials in a tool input.
Find the membership with one list page:
```json
{"channelCode":"selected-channel-code","view":{"kind":"list","page":1,"limit":15}}
```
Follow returned pagination when needed; a page is not the complete inventory. Copy the target's mid, not its numeric row ID, member/profile ID or product ID. Read its basic detail:
```json
{"channelCode":"selected-channel-code","view":{"kind":"detail","membershipMid":"selected-general-mid"}}
```
Confirm the intended membership and its supported stored type, title, all five benefits, card fields and installed-feature associations. Copy the top-level revision. Unknown stored types fail validation. A changed known type invalidates the pre-write revision, and a mismatched type in the receipt or independent read leaves the sent outcome unresolved. Do not coerce type, recreate the target or resend after a backend rejection or uncertain result.
## Plan and apply the requested patch
Combine the user's intended presentation changes in one call. For example, plan a new title, two benefit changes and card layout B:
```json
{
"requestId":"membership-edit-001",
"channelCode":"selected-channel-code",
"membershipMid":"selected-general-mid",
"expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000",
"patch":{
"title":"Community Membership",
"benefits":{"benefitAlpha":"Member updates","benefitBravo":""},
"card":{"designSchema":"B"}
},
"dryRun":true
}
```
Replace the selectors and illustrative revision with the read values. Inspect PLAN_READY, changedFields, noop, wouldWrite and the complete preserved body. Empty benefitBravo clears only that statement. Omitted benefits, overlay, images and installed-feature associations stay unchanged. Benefit text is descriptive and does not grant access. The tool cannot replace images. To change associations explicitly, use the access-selection section below.
A supplied title is trimmed and must be 4–50 characters. Supplied benefit strings allow 0–50 characters. Card selection accepts A/B while an omitted stored C remains unchanged. Explicit overlay NONE clears both colors; SOLID requires primary and clears secondary; GRADIENT requires both colors. Colors are six/eight-digit hex strings. Empty nested patches, null clear instructions and unknown fields are rejected. See the tool page for a complete overlay example.
When the planned edit is intended, submit the same logical request with dryRun false. Keep its requestId, target, patch and expectedRevision. A plan creates no journal and reserves no state. A fresh comparison can reject an intervening edit before PUT; after such a rejection, reread and reconsider the change. There is no backend conditional-write guarantee.
An unchanged request can complete with MEMBERSHIP_METADATA_UNCHANGED and no PUT. A changed request returns MEMBERSHIP_METADATA_UPDATED only after a validated receipt and independent read match the requested and preserved state. Call the basic detail reader again for current values; a completed task response can be historical. Compare business fields while allowing the server's updatedAt to change.
## Keep uncertainty on the same task
For status or resume, retain the original task identity:
```json
{"taskType":"membership-metadata","taskId":"membership-edit-001"}
```
Both operations reauthorize the saved context. Resume is read-only for this workflow. A prepared task can report MEMBERSHIP_METADATA_NOT_DISPATCHED; only the identical original update call may attempt an intended unsent edit after its checks. Once sent is recorded, update replay and resume observe the known mid and never send the PUT again, including after an explicit error response. Reconciliation requires the complete desired and preserved state; it does not prove who caused that state.
Do not switch to a new task ID to retry an uncertain write, match another membership by title, or roll back automatically. Status cannot invent a missing receipt. Task input/context/state errors require resolving the saved task and connection. A completed replay is historical, so use a fresh get for present state. If restoration is intended and authorized, fresh-read first, use a separate task/current revision and restore only the changed fields while preserving intervening edits.
## Observe the correct boundary
A successful plan or completion exposes one membership-editor console destination when the shared wrapper can resolve it. Pending TASK_STATUS, errors and demo provide no usable destination. That address does not perform the business action or certify browser access; the hosting interface remains for connection and Reference use.
Overlay mutation, nonempty association preservation, genuine uncertain-write recovery, all role combinations, visitor entitlement, full card rendering and deployed hosted isolation remain unverified.
## Later shared GET limit
This affects reads used by the existing metadata workflow as well as the [serial-key reader](membership-serial-keys.md). It changes neither metadata inputs, permitted patches, uncertain-write reconciliation nor task identity. An oversized/malformed read remains a failed observation; it does not authorize replaying a sent PUT or starting a new task. The writer's earlier actual change/restoration evidence is not expanded to new failures or hosted deployment by this transport correction.
## Membership access selection
Read `memberships_get` with `view:{kind:"detail",membershipMid,includeAccessOptions:true}`.
Copy the top-level membership revision and the separate `accessOptions.revision`.
The option list contains installed numeric IDs, codes, stored aliases, activation
state, `canSelect` and `canRetain`. It uses the original membership reducer and
store-default filters: X00001/X00003/X00005/F00001, Z-prefixed and 3RD_-prefixed
features cannot be newly selected. Inactive eligible features remain selectable;
no activation is performed. A filtered existing association can be retained only
while its current installed ID/code still match. Missing installed IDs are refused
for an explicit replacement; omit access to preserve an untouched existing set.
Use `membership_update.patch.access` with `installedPAppIds` (the complete desired
set) and `expectedOptionsRevision`. Omission preserves all current associations.
An empty array requires `confirmEmpty:true`, matching the original empty-selection
confirmation. Combine access with title, benefits or styling in the same request.
The planned body and access.before/after show what will change. All other editor
fields are preserved. No subscription price, product, purchase, feature ACL or
installed-feature activation changes are performed.
Options are read twice for discovery and rechecked before dispatch. Changed
choices return MEMBERSHIP_ACCESS_OPTIONS_CHANGED; invalid/missing target choices
return MEMBERSHIP_ACCESS_TARGET_UNAVAILABLE. Duplicated or malformed option
identities fail without a partial list. Explicit returned channel IDs must match;
otherwise channel binding comes from the scoped route/header/credential, not an
independently returned identity. Metadata-only requests do not add inventory reads.
The existing one-PUT journal records the selected options for recovery. Sent requests are never resent; resume only reads the known membership and compares its exact desired ID/code set and preserved fields. Old metadata-only journals remain compatible. Matching stored associations do not prove visitor entitlement, content-level access, causal ownership or atomic compare-and-swap.
## Create a GENERAL membership
Use `memberships_get` with `view:{kind:"create-options"}` to obtain built-in card defaults, current selectable installed-feature IDs and `accessOptions.revision` without an existing membership. Then use [membership_create](../tools/membership_create.md) to submit title, card styling, benefits and the complete access selection together. The writer validates the receipt and reads its exact mid independently; uncertain creation is not retried or matched by title. This supersedes earlier creation-is-unimplemented wording for built-in artwork. Omission retains built-ins. Requested uploads are acknowledged before one full POST and an independent receipt-mid read. Existing-membership image controls use membership_update for any of the four supported stored types, without changing type.
## Reset a card background or remove a logo
The current writer accepts `patch.card.backgroundImage:"reset-default"` and
`patch.card.logoImage:"remove"`. These source controls share the existing read,
revision, dry-run and one-PUT recovery workflow. Omission preserves a slot; reset
uses the source built-in background, and removal clears only the displayed logo.
It does not delete any uploaded file. Original-byte replacement is now supported for existing memberships; arbitrary URL replacement remains unsupported. Restoring custom artwork needs its original file bytes and a fresh, separately intended update.
## Replace existing membership artwork
These can accompany metadata, styling and access selection in the same full save. The logo limit is bridge policy. The VOD console-upload direction does not change this contract.
Dry run validates without credentials, upload or save and cannot return final image URLs: body is null with bodyRequiresUploadReceipts, plannedUploads and preservedBodyBeforeUploads. On an intended update, each image acknowledgement precedes the complete membership PUT. Keep the original requestId and bytes for unattempted effects; status/resume only observe, uncertain grants/uploads never repeat, and a sent final save only reconciles the known membership.
Image transfers and the final save are not atomic. Interruption can leave an orphan object, with no automatic cleanup or rollback.