publ betaReference (MCP guide)
Open publ console

publ Reference

Migration map.

legacy_promotion_link_apply

See the current correction and older-server guidance before preparing new inputs; keep existing task inputs unchanged during recovery.

This implementation is a dependency of the documentation branch.

Compose legacy link creation or update selected sharing metadata and an optional original thumbnail. Default cards are read-only. There is no activation, bulk mutation, QR export or associated-page editor in this workflow.

Access and input

Requires OWNER or DELEGATEE with MANAGE_CONVERSION_LINKS for the selected channel. Hosted source rules expose these tools and legacy-promotion-link recovery only on the exact selected-channel resource, not account resources. This is not deployed authorization evidence.

channelCode is a string of 1–512 characters, excluding literal dot/double dot, slash, backslash, ASCII controls and DEL. Selected input IDs are positive safe integers from authorized results. Strict inputs reject unknown fields. These legacy links are separate from conversion links; do not exchange their IDs, revisions, share bases or task types.

Input Contract
channelCode Required selected channel.
requestId Required 8–80 ASCII letters, digits, underscores or hyphens. Keep original input with this ID.
expectedRevision Required 64 lowercase hexadecimal characters; list.creationRevision for create, detail.revision for update.
action create or update.
id Forbidden for create; required selected positive safe integer for update.
settings Complete create settings or at least one editable update field.
dryRun Optional boolean; default false. true checks a plan without writes or a new task.

Runtime refinements enforce action/field combinations beyond the flat JSON export. Replace sample IDs and zero revisions with selected read results.

{
  "channelCode": "selected-channel-code",
  "requestId": "legacy-create-01",
  "action": "create",
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "settings": {
    "name": "Autumn promotion",
    "title": "See our latest content",
    "description": "Explore the channel.",
    "keyword": "autumn_2026"
  },
  "dryRun": true
}
{
  "channelCode": "selected-channel-code",
  "requestId": "legacy-update-01",
  "action": "update",
  "id": 123,
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "settings": {
    "title": "Updated sharing title"
  },
  "dryRun": true
}

Settings and preservation

Field Contract
settings.name Trimmed 1–50 characters; required for create, optional for update.
settings.title Trimmed 1–70 characters; required for create, optional for update.
settings.description Trimmed 1–160 characters; required for create, optional for update.
settings.keyword Required for create only: 3–30 ASCII letters, digits, underscores or hyphens; case is preserved.
settings.thumbnail Optional original PNG/JPEG object; null/removal and replacement URLs are unsupported.
settings.thumbnail.filename Basename of 1–240 characters with .png, .jpg or .jpeg extension; no slash, backslash, colon, ASCII controls or DEL. Use lowercase extensions with exported JSON-schema clients.
settings.thumbnail.base64 Canonical bare base64, 4–13,333,332 characters; decoded original bytes must also pass validation.

Update preserves omitted values and submits a PUT body containing complete name/title/description from requested values plus baseline, and thumbnailSrc only after a verified replacement upload. Keyword, targetMdid, targetPageId and unknown stored business fields remain read-only and must survive full readback. Only derived linkTime is excluded.

Creation uses a new chosen keyword and the source default thumbnail when omitted. Creation receipt requires numeric positive safe-integer id/channelId matching the selected channel, and an id absent from the creation baseline. The conversion workflow's canonical-string receipt correction does not widen legacy receipts. A missing durable identity is never adopted by name or keyword.

Creation quota and availability

A new create plan checks a stable selected-channel quota and complete bounded ID baseline against creationRevision. Unknown policy returns LEGACY_LINK_POLICY_UNVERIFIED; exhausted quota returns LEGACY_LINK_QUOTA_REQUIRED. Before a task is created these return action_required with one human channel-plan destination and writesPerformed:false. No plan purchase, billing API or benefit change is sent.

Actual create rechecks quota/inventory before image work and again before submission. Keyword availability is a POST to the source /check endpoint accepting HTTP200 without a body; it runs before image work and again before creation. It may be checked again while untouched work continues. Availability is not a reservation and is not checked by dry run. A changed baseline requires a fresh read; task input/revisions cannot be silently changed. Existing-link update is not subject to the creation quota.

Original thumbnail

Original PNG/JPEG bytes must be strictly below 10,000,000 decimal bytes (maximum 9,999,999). Filename/format/signature, canonical base64, byte length and SHA-256 are verified. No data URL, remote image URL, caller-supplied credential or forged receipt is accepted. Omission preserves an existing image. The authenticated MCP envelope is 16 MiB, independent of client/proxy limits and individual image bounds.

The host must configure resource gateway, bucket and region before an image plan/effect. Issuance delegates to the strict shared conversion grant200/201 adapter; upload uses original bytes and validates the receipt against metadata and generated location. Only receipt.location enters thumbnailSrc. Journals contain metadata/receipts, never base64, file bytes or temporary credentials. Upload/save are separate effects with no rollback or orphan deletion.

{
  "channelCode": "selected-channel-code",
  "requestId": "legacy-thumbnail-01",
  "action": "update",
  "id": 123,
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "settings": {
    "thumbnail": {
      "filename": "cover.png",
      "base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aDWQAAAAASUVORK5CYII="
    }
  },
  "dryRun": true
}

Inputs

Field Required here Contract
action Yes create, update
id No integer; greater than 0
settings Yes object or object
settings.name Yes When settings is alternative 1: string; min length 1; max length 50
settings.title Yes When settings is alternative 1: string; min length 1; max length 70
settings.description Yes When settings is alternative 1: string; min length 1; max length 160
settings.keyword Yes When settings is alternative 1: string; pattern ^[A-Za-z0-9_-]{3,30}$
settings.thumbnail No When settings is alternative 1: one of: local-file, more-parts, complete, cancel (by kind)
settings.thumbnail.filename Yes When settings is alternative 1 and settings.thumbnail.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.
settings.thumbnail.byteLength Yes When settings is alternative 1 and settings.thumbnail.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat).
settings.thumbnail.sha256 No When settings is alternative 1 and settings.thumbnail.kind is local-file: string; pattern ^[0-9a-f]{64}$
settings.thumbnail.uploadId Yes When settings is alternative 1 and settings.thumbnail.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$
settings.thumbnail.fromPart Yes When settings is alternative 1 and settings.thumbnail.kind is more-parts: integer; minimum 1; maximum 10000
settings.thumbnail.uploadId Yes When settings is alternative 1 and settings.thumbnail.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$
settings.thumbnail.parts Yes When settings is alternative 1 and settings.thumbnail.kind is complete: array; min items 1; max items 10000
settings.thumbnail.parts[].partNumber Yes When settings is alternative 1 and settings.thumbnail.kind is complete: integer; minimum 1; maximum 10000
settings.thumbnail.parts[].etag Yes When settings is alternative 1 and settings.thumbnail.kind is complete: string; min length 1; max length 200
settings.thumbnail.measured No When settings is alternative 1 and settings.thumbnail.kind is complete: object; What the video measures on this computer, read with measureCommand.
settings.thumbnail.measured.durationSeconds Yes When settings is alternative 1 and settings.thumbnail.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration).
settings.thumbnail.measured.frameRate Yes When settings is alternative 1 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97).
settings.thumbnail.measured.width Yes When settings is alternative 1 and settings.thumbnail.kind is complete: integer; maximum 100000; greater than 0
settings.thumbnail.measured.height Yes When settings is alternative 1 and settings.thumbnail.kind is complete: integer; maximum 100000; greater than 0
settings.thumbnail.measured.frameRateMode No When settings is alternative 1 and settings.thumbnail.kind is complete: CFR, VFR
settings.thumbnail.measured.minFrameRate No When settings is alternative 1 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0
settings.thumbnail.measured.maxFrameRate No When settings is alternative 1 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0
settings.thumbnail.uploadId Yes When settings is alternative 1 and settings.thumbnail.kind is cancel: string; pattern ^upl_[A-Za-z0-9_-]{22}$
settings.name No When settings is alternative 2: string; min length 1; max length 50
settings.title No When settings is alternative 2: string; min length 1; max length 70
settings.description No When settings is alternative 2: string; min length 1; max length 160
settings.thumbnail No When settings is alternative 2: one of: local-file, more-parts, complete, cancel (by kind)
settings.thumbnail.filename Yes When settings is alternative 2 and settings.thumbnail.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.
settings.thumbnail.byteLength Yes When settings is alternative 2 and settings.thumbnail.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat).
settings.thumbnail.sha256 No When settings is alternative 2 and settings.thumbnail.kind is local-file: string; pattern ^[0-9a-f]{64}$
settings.thumbnail.uploadId Yes When settings is alternative 2 and settings.thumbnail.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$
settings.thumbnail.fromPart Yes When settings is alternative 2 and settings.thumbnail.kind is more-parts: integer; minimum 1; maximum 10000
settings.thumbnail.uploadId Yes When settings is alternative 2 and settings.thumbnail.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$
settings.thumbnail.parts Yes When settings is alternative 2 and settings.thumbnail.kind is complete: array; min items 1; max items 10000
settings.thumbnail.parts[].partNumber Yes When settings is alternative 2 and settings.thumbnail.kind is complete: integer; minimum 1; maximum 10000
settings.thumbnail.parts[].etag Yes When settings is alternative 2 and settings.thumbnail.kind is complete: string; min length 1; max length 200
settings.thumbnail.measured No When settings is alternative 2 and settings.thumbnail.kind is complete: object; What the video measures on this computer, read with measureCommand.
settings.thumbnail.measured.durationSeconds Yes When settings is alternative 2 and settings.thumbnail.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration).
settings.thumbnail.measured.frameRate Yes When settings is alternative 2 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97).
settings.thumbnail.measured.width Yes When settings is alternative 2 and settings.thumbnail.kind is complete: integer; maximum 100000; greater than 0
settings.thumbnail.measured.height Yes When settings is alternative 2 and settings.thumbnail.kind is complete: integer; maximum 100000; greater than 0
settings.thumbnail.measured.frameRateMode No When settings is alternative 2 and settings.thumbnail.kind is complete: CFR, VFR
settings.thumbnail.measured.minFrameRate No When settings is alternative 2 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0
settings.thumbnail.measured.maxFrameRate No When settings is alternative 2 and settings.thumbnail.kind is complete: number; maximum 1000; greater than 0
settings.thumbnail.uploadId Yes When settings is alternative 2 and settings.thumbnail.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.

Results and task recovery

Create POST accepts HTTP200/201 data; update PUT requires HTTP200 data. Acknowledgement is followed by full selected-record readback, not treated as completion. Stable selected context and full before content are checked before effects. A metadata-only no-op records done without a write; a supplied thumbnail remains a separate effect.

New dry run returns LEGACY_LINK_PLAN, dryRun:true, noop, changes, keywordAvailability:not-checked-by-dryRun, plannedStages, writesPerformed:false and verification.confirmed:false. It creates no new task or keyword check. Saved jobs use taskType legacy-promotion-link and the original requestId as taskId. Normal results use LEGACY_LINK_COMPLETE or LEGACY_LINK_PENDING with phase, historical, noop, acknowledged and writesPerformed. After done they include promotionLink, revision and shareUrl; created/upload metadata appear when recorded.

writesPerformed is false for no-op/prepared, true after a durable upload receipt or acknowledged write, and null in other pending states, including uploaded preparation with no receipt or acknowledgement. Fresh completion can confirm full stored readback. Historical completion has confirmed:false; storedCompletion is separate. atomicSnapshot, redirectBehaviorVerified, associatedPageVerified and imageAvailabilityVerified remain false.

Phase Continuation contract
prepared Revalidate context/content or creation quota/inventory; untouched image work after restart requires original apply bytes.
grant-sent or upload-sent Preserve and inspect; attempted grant/upload is never repeated, even after a definite failure.
uploaded A durable receipt or finished no-image preparation permits the untouched write after fresh checks.
write-sent Readback only. A known ID can reconcile; a create without durable returned identity stays LEGACY_CREATE_UNKNOWN, without name/keyword adoption or resend.
done Immutable historical status/resume; confirmed:false. Read again for current state.

Status does not dispatch effects. Resume may continue untouched stages; sent create/update/delete/grant/upload never repeats. Pending results may contain lastResult, requiredAction and journalPersistence. A changed input under the same requestId returns REQUEST_ID_CONFLICT; malformed saved identity/progress returns TASK_STATE_UNVERIFIED. Preserve the original task instead of forcing a new ID after uncertainty. No automatic recache runs.

Navigation opens the legacy create route until a returned ID is durable, then /{id}/edit; update opens edit, and preflight quota/policy failure opens the human plan page. Demo omits live links. No associated page/membership editing or remote cleanup is implied.

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.