marketing_banner_apply
See the current correction and older-server guidance before preparing new inputs; keep existing task inputs unchanged during recovery.
Create a default banner, change visibility, or compose content/image/visibility changes through one tool. The selected object is globalBanner; the other widget and all unrelated custom settings must be preserved.
Access and preparation
Requires OWNER or DELEGATEE with MANAGE_MARKETING_TOOLS for the selected channel. An unrelated permission or CREATOR role is insufficient. The source-defined hosted selected-channel resource admits this tool only for its exact channelCode; the account resource excludes it.
Read marketing_banner_get first and retain its exact revision. New plans independently reread the full custom record and plan, then verify channel/context continuity. The revision includes unrelated settings, timestamps and plan; a change returns MARKETING_CONFLICT before dispatch. Later plan changes return MARKETING_PLAN_CHANGED and pause remaining effects.
The host configures CHANNEL_ENV. Replacement images additionally require a resource gateway, upload bucket and region, including for dry runs. No DNS/recache service is used. A no-op skips mutation-service validation but still performs preparation reads. Service URLs and credentials are not inputs.
Inputs and actions
The outer object, settings, images and each image object are strict. Action-specific combinations are runtime refinements beyond the exported flat object schema.
| Field | Required here | Contract |
|---|---|---|
action |
Yes | create, visibility, configure; create: source defaults only if absent; visibility: change visible only; configure: merge settings/images with source plan eligibility. |
visible |
No | boolean |
settings |
No | object |
settings.title |
No | string; max length 30; Empty or 2–30 UTF-16 code units, matching the source editor. Emoji may occupy two units. Omit to preserve the saved value. |
settings.description |
No | string; max length 40; Empty or 2–40 UTF-16 code units, matching the source editor. Emoji may occupy two units. Omit to preserve the saved value. |
settings.title_color |
No | string; min length 1; max length 128 |
settings.description_color |
No | string; min length 1; max length 128 |
settings.bg_color |
No | string; min length 1; max length 128 |
settings.conversion_link_url |
No | string; max length 4096 |
settings.conversion_link_type |
No | WEB_LINK, INTERNAL_LINK |
settings.design_template_code |
No | GLOBAL_BANNER_TYPE_A, GLOBAL_BANNER_TYPE_B, GLOBAL_BANNER_TYPE_C |
images |
No | object |
images.desktop_bg_image_src |
No | one of: local-file, more-parts, complete, cancel (by kind) or null |
images.desktop_bg_image_src.filename |
Yes | When images.desktop_bg_image_src.kind is local-file: string; min length 1; max length 240; pattern ^[^/\\:\x00-\x1f\x7f]+$; The name of the file on this computer, with its extension. |
images.desktop_bg_image_src.byteLength |
Yes | When images.desktop_bg_image_src.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat). |
images.desktop_bg_image_src.sha256 |
No | When images.desktop_bg_image_src.kind is local-file: string; pattern ^[0-9a-f]{64}$ |
images.desktop_bg_image_src.uploadId |
Yes | When images.desktop_bg_image_src.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
images.desktop_bg_image_src.fromPart |
Yes | When images.desktop_bg_image_src.kind is more-parts: integer; minimum 1; maximum 10000 |
images.desktop_bg_image_src.uploadId |
Yes | When images.desktop_bg_image_src.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
images.desktop_bg_image_src.parts |
Yes | When images.desktop_bg_image_src.kind is complete: array; min items 1; max items 10000 |
images.desktop_bg_image_src.parts[].partNumber |
Yes | When images.desktop_bg_image_src.kind is complete: integer; minimum 1; maximum 10000 |
images.desktop_bg_image_src.parts[].etag |
Yes | When images.desktop_bg_image_src.kind is complete: string; min length 1; max length 200 |
images.desktop_bg_image_src.measured |
No | When images.desktop_bg_image_src.kind is complete: object; What the video measures on this computer, read with measureCommand. |
images.desktop_bg_image_src.measured.durationSeconds |
Yes | When images.desktop_bg_image_src.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
images.desktop_bg_image_src.measured.frameRate |
Yes | When images.desktop_bg_image_src.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
images.desktop_bg_image_src.measured.width |
Yes | When images.desktop_bg_image_src.kind is complete: integer; maximum 100000; greater than 0 |
images.desktop_bg_image_src.measured.height |
Yes | When images.desktop_bg_image_src.kind is complete: integer; maximum 100000; greater than 0 |
images.desktop_bg_image_src.measured.frameRateMode |
No | When images.desktop_bg_image_src.kind is complete: CFR, VFR |
images.desktop_bg_image_src.measured.minFrameRate |
No | When images.desktop_bg_image_src.kind is complete: number; maximum 1000; greater than 0 |
images.desktop_bg_image_src.measured.maxFrameRate |
No | When images.desktop_bg_image_src.kind is complete: number; maximum 1000; greater than 0 |
images.desktop_bg_image_src.uploadId |
Yes | When images.desktop_bg_image_src.kind is cancel: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
images.mobile_bg_image_src |
No | one of: local-file, more-parts, complete, cancel (by kind) or null |
images.mobile_bg_image_src.filename |
Yes | When images.mobile_bg_image_src.kind is local-file: string; min length 1; max length 240; pattern ^[^/\\:\x00-\x1f\x7f]+$; The name of the file on this computer, with its extension. |
images.mobile_bg_image_src.byteLength |
Yes | When images.mobile_bg_image_src.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat). |
images.mobile_bg_image_src.sha256 |
No | When images.mobile_bg_image_src.kind is local-file: string; pattern ^[0-9a-f]{64}$ |
images.mobile_bg_image_src.uploadId |
Yes | When images.mobile_bg_image_src.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
images.mobile_bg_image_src.fromPart |
Yes | When images.mobile_bg_image_src.kind is more-parts: integer; minimum 1; maximum 10000 |
images.mobile_bg_image_src.uploadId |
Yes | When images.mobile_bg_image_src.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
images.mobile_bg_image_src.parts |
Yes | When images.mobile_bg_image_src.kind is complete: array; min items 1; max items 10000 |
images.mobile_bg_image_src.parts[].partNumber |
Yes | When images.mobile_bg_image_src.kind is complete: integer; minimum 1; maximum 10000 |
images.mobile_bg_image_src.parts[].etag |
Yes | When images.mobile_bg_image_src.kind is complete: string; min length 1; max length 200 |
images.mobile_bg_image_src.measured |
No | When images.mobile_bg_image_src.kind is complete: object; What the video measures on this computer, read with measureCommand. |
images.mobile_bg_image_src.measured.durationSeconds |
Yes | When images.mobile_bg_image_src.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
images.mobile_bg_image_src.measured.frameRate |
Yes | When images.mobile_bg_image_src.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
images.mobile_bg_image_src.measured.width |
Yes | When images.mobile_bg_image_src.kind is complete: integer; maximum 100000; greater than 0 |
images.mobile_bg_image_src.measured.height |
Yes | When images.mobile_bg_image_src.kind is complete: integer; maximum 100000; greater than 0 |
images.mobile_bg_image_src.measured.frameRateMode |
No | When images.mobile_bg_image_src.kind is complete: CFR, VFR |
images.mobile_bg_image_src.measured.minFrameRate |
No | When images.mobile_bg_image_src.kind is complete: number; maximum 1000; greater than 0 |
images.mobile_bg_image_src.measured.maxFrameRate |
No | When images.mobile_bg_image_src.kind is complete: number; maximum 1000; greater than 0 |
images.mobile_bg_image_src.uploadId |
Yes | When images.mobile_bg_image_src.kind is cancel: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
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. |
irreversibleConfirmed |
No | true; true only after the person confirmed again, when the preview has requiresReconfirmation: the action cannot be undone. |
create accepts no visible/settings/images. It writes source defaults only when this widget is absent; an existing widget is a no-op. Defaults start NOT_ACTIVATED. visibility requires an existing widget and visible; settings/images are forbidden. It changes only is_activated while retaining the complete existing content.
configure merges supplied settings/images and optional visible into one final nested save. It requires at least one supplied setting, image slot or visible, checked by the workflow after schema parsing. Missing widgets start from source defaults. Even a configure containing only visible or unchanged values requires a recognized eligible editor plan. Use visibility for the separate visibility action. Denied or unknown plans return action_required / MARKETING_EDIT_PLAN_REQUIRED with the exact screen's human-console guidance and no writes. No plan upgrade or payment occurs. See the complete eligibility list.
Settings
| Field | Contract |
|---|---|
| settings.title | Empty or 2–30 characters. |
| settings.description | Empty or 2–40 characters. |
| settings.title_color | Nonempty string, 1–128 characters, no ASCII control or DEL; not a complete CSS color validator. |
| settings.description_color | Same color constraint. |
| settings.bg_color | Same color constraint. |
| settings.conversion_link_url | Empty or a normalized HTTP(S) destination satisfying the source pattern; rules below. |
| settings.conversion_link_type | WEB_LINK or INTERNAL_LINK. |
| settings.design_template_code | GLOBAL_BANNER_TYPE_A, GLOBAL_BANNER_TYPE_B or GLOBAL_BANNER_TYPE_C. |
Creation defaults are title TITLE, description DESCRIPTION, title_color and description_color #FFFFFF, bg_color #000000, empty desktop/mobile images, https://publ.co with WEB_LINK, and GLOBAL_BANNER_TYPE_B. They are creation values, never fallbacks for malformed reads. There is no per-slot visibility or widget deletion action.
Text is not trimmed; empty is allowed where listed, and a single character is rejected. Supplied links are limited to 4,096 characters before removing all whitespace and adding https:// when no HTTP(S) prefix exists. Nonempty results require HTTP(S), a hostname, no embedded credentials and the source domain-pattern match. This is validation/normalization, not URL fetching or destination availability verification. WEB_LINK and INTERNAL_LINK retain the source's new-tab/current-tab choice; INTERNAL_LINK does not accept a separate page-ID field.
Images
| Field | Contract |
|---|---|
| images.desktop_bg_image_src | Omit to preserve; null removes the reference; otherwise an original PNG/JPEG object. |
| images.desktop_bg_image_src.filename | Basename, 1–240 characters, PNG/JPG/JPEG extension; no path, URL, colon or control characters. |
| images.desktop_bg_image_src.base64 | Canonical original-file base64, 4–6,990,508 characters; no data URL or whitespace. |
| images.mobile_bg_image_src | Omit to preserve; null removes the reference; otherwise an original PNG/JPEG object. |
| images.mobile_bg_image_src.filename | Basename, 1–240 characters, PNG/JPG/JPEG extension; no path, URL, colon or control characters. |
| images.mobile_bg_image_src.base64 | Canonical original-file base64, 4–6,990,508 characters; no data URL or whitespace. |
All replacement files together may contain at most 5 MiB of original bytes. This aggregate cap is a bridge policy for its 8 MiB request envelope, not a source-widget file-size rule. Extension matching is case-insensitive at runtime but the exported regex omits the flag; use lowercase filenames for both. Parsed bytes must match PNG/JPEG structure and extension. The bridge does not resize or render them. null stores an empty image reference and sends no upload; arbitrary replacement image URLs are not accepted.
Do not retry a pending image with a new request ID.
Input examples
These illustrate dry-run inputs, not observed service results. Replace the channel/revision and choose the user's intended content.
{
"channelCode": "selected-channel-code",
"requestId": "banner-create-plan-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"action": "create",
"dryRun": true
}
{
"channelCode": "selected-channel-code",
"requestId": "banner-visibility-plan-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"action": "visibility",
"dryRun": true,
"visible": false
}
{
"channelCode": "selected-channel-code",
"requestId": "banner-configure-plan-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"action": "configure",
"dryRun": true,
"settings": {
"title": "Welcome",
"description": "Latest news"
}
}
{
"channelCode": "selected-channel-code",
"requestId": "banner-image-plan-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"action": "configure",
"dryRun": true,
"images": {
"desktop_bg_image_src": {
"filename": "banner.png",
"base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aDWQAAAAASUVORK5CYII="
}
}
}
Effects and results
A new dry run returns MARKETING_WIDGET_PLAN, noop, normalized changes and plannedStages, with no task creation or business writes. Image bytes are replaced with metadata in the planned changes. With an existing matching task, dryRun returns historical saved progress as a plan rather than a fresh business preview.
A real no-op writes a completed journal without business requests. For a change, the workflow can initialize a missing custom record with a bodyless HTTP200 POST to /seller/api/v1/channel-management/custom, retain its observed full baseline, issue/upload each replacement image, then PUT /seller/api/v1/channel-management/custom/experimentals with one nested globalBanner object. That object merges requested values with preserved fields; it is not a replacement of the whole experimentals object. The save expects HTTP200 without a response-body contract. There is no recache step.
The final readback must preserve every unrelated business setting, the other widget, unknown nested JSON, record ID, channel ID and insertedAt. updatedAt may change during the save. Only top-level experimentals.id/exp_id are excluded. The workflow checkpoints before each effect, but does not use a server-side compare-and-swap or transaction. Failed uploads/saves can leave initialized records or orphaned objects; no rollback or automatic deletion occurs.
MARKETING_WIDGET_COMPLETE includes widget, revision, phase done, historical, initializationAcknowledged, settingsSaveAcknowledged and uploads. Fresh completion confirms stored readback only. A no-op has no save acknowledgement; readback-only reconciliation can also complete without a save acknowledgement or proven causality. writesPerformed is false for a no-op or untouched prepared phase, true when an initialization/save or uploaded-object receipt exists, and otherwise null.
MARKETING_WIDGET_PENDING retains phase, uploads, lastResult and inspect_task guidance. journalPersistence.saved:false means the error checkpoint was not saved. Authentication/context errors can suppress channel/navigation metadata. Image receipts contain generated storage coordinates and checksums, not raw bytes or temporary credentials.
Task recovery
Use taskType marketing-widget and the original taskId with status or resume. The task binds widget kind, channel/context, original revision/plan, normalized input fingerprint, record snapshots and image metadata. The same requestId cannot switch between banner and popup or change the input. Unknown or inconsistent journals fail validation; do not edit or rename them to bypass it.
| Phase | Recovery behavior |
|---|---|
| prepared | Recheck the original snapshot/plan, then start untouched work. |
| init-sent | Read back the custom record only; observed initialization can advance without an acknowledgement. Never resend initialization. |
| initialized | Recheck the full baseline, handle any untouched image stages, then save. |
| Per-image prepared | Recall the original apply with identical bytes if they are no longer in memory; resume alone returns MARKETING_IMAGE_REQUIRED. |
| Per-image grant-sent / upload-sent | Hold with MARKETING_UPLOAD_UNKNOWN for inspection; never reissue the grant or repeat upload. |
| Per-image uploaded | Retain the verified receipt and continue untouched work. |
| save-sent | Reconcile from full readback only. Match can complete with settingsSaveAcknowledged:false; mismatch holds with MARKETING_SAVE_UNKNOWN. Never resend save. |
| done | Return historical:true and verification.confirmed:false after authority checks. Read the screen for current state. |
Status never advances effects. Resume may run untouched stages; no sent boundary is automatically reset, even after definite failure. A rejected continuation of a completed task returns its error without writing an error detail into the completed journal.
The marketing-widget namespace uses the selected task store. For a deliberate later restoration, obtain a fresh reader revision and separately authorized input; retain uncertain tasks.
Saved record identity
A missing record differs from an existing malformed record. Stored JSON null,
false, zero or an empty string is rejected with TASK_STATE_UNVERIFIED by the
existing record validator. Apply/dry-run/status/resume must not overwrite it or
treat it as a fresh task. Preserve the file for inspection; do not delete/edit it
or replace the request ID to bypass the error. Existing valid task recovery and
genuinely missing-record behavior remain unchanged.