membership_create
Create one GENERAL membership with the original wizard's title, card styling, five optional benefit statements, optional original card images and installed-feature selection in one operation. Artwork implementation:
Prepare the choices
Call memberships_get without an existing membership:
{"channelCode":"selected-channel-code","view":{"kind":"create-options"}}
The response includes built-in cardDefaults and accessOptions. Choose installed
numeric IDs whose canSelect is true and copy accessOptions.revision. This is a
separate installed-feature revision, not an existing membership revision. An inactive
eligible feature can be selected without being activated. Filtered features cannot
be newly granted; do not copy display names or store codes into the ID array.
Compose and submit
Replace the example IDs and revision with returned values. Without image uploads,
dryRun:true returns the complete planned request. With uploads, it returns the
pre-upload plan described below; no grant, upload, POST, new task record or reservation is made.
After reviewing the plan, send the same choices with dryRun:false or omit it.
Keep the same requestId for the logical operation.
The trimmed title must contain 4–50 UTF-16 code units. Five optional statements
(benefitAlpha through benefitEcho) each allow up to 50 units. Empty or omitted
statements are omitted from the source POST and must read back as empty strings.
Benefit text alone grants no feature access. Omitted image slots keep the source built-in
background and logo; explicit uploads or default-logo removal are supported below.
Omitted card styling uses A with SOLID #455EEF4D; selectable designs are A/B.
Overlays are NONE, SOLID with primary, or GRADIENT with both primary and
secondary; colors use six/eight-digit hex. Arbitrary image URLs remain unsupported.
access.installedPAppIds is the complete desired set. An intentionally empty set
requires confirmEmpty:true. Missing, duplicate, filtered or changed choices fail
before dispatch. OWNER or DELEGATEE with MANAGE_MONETIZATION is required in the
exact selected channel. The original create control adds no paid-plan or active-
monetization prerequisite, so the bridge does not invent one. Backend rejection
still fails; it is not converted into completion.
After any requested image uploads are acknowledged, the workflow rechecks context/options, records dispatch, sends one complete POST, saves the validated receipt and independently reads its exact membership mid. Success is MEMBERSHIP_CREATED only when identity, title, all card/benefit fields and exact installed ID/code associations agree. It creates no subscription product, price, purchase, payment, feature activation or ACL change. Use membership_update for later supported edits and membership_display_apply for a separate display choice.
Create with original card artwork
The image fields belong directly inside card, not inside an existing-membership patch. They can accompany card styling, benefits and the complete access selection in the same creation request.
| Input | Accepted value and effect |
|---|---|
| card.backgroundImage | Omit for the built-in background, use "reset-default" to select it explicitly, or {kind:"upload",file:{filename,base64}} for original PNG/JPEG/GIF up to and including 5 MiB (5242880 bytes). |
| card.logoImage | Omit for the built-in logo, use "remove" to create with an empty logo reference, or {kind:"upload",file:{filename,base64}} for original PNG/SVG up to 1 MiB (1048576 bytes). |
The background limit follows the source chooser; the logo limit is bridge transport policy. Supply a matching basename and canonical base64 without a local path, URL, data-URL prefix or whitespace. Original bytes are preserved without resizing. The shared parser checks bounded file/container structure, not complete decoding, SVG sanitization or browser rendering. The host must configure the channel-bound resource gateway, bucket and region; the caller supplies no credentials or guessed pApp consumer. Missing configuration returns UPLOAD_NOT_CONFIGURED.
This example reuses tiny illustrative PNG/SVG files to show the input shape. Replace them with the intended original artwork, and replace the installed IDs/options revision from create-options. It is a dry-run example, not evidence of an upload or a rendered membership card.
{
"requestId": "membership-create-artwork-001",
"channelCode": "selected-channel-code",
"title": "Community membership",
"access": {
"installedPAppIds": [
17,
24
],
"expectedOptionsRevision": "0000000000000000000000000000000000000000000000000000000000000000"
},
"dryRun": true,
"card": {
"designSchema": "B",
"backgroundImage": {
"kind": "upload",
"file": {
"filename": "example-background.png",
"base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNwjXv/HwAFEAKS9fMIJwAAAABJRU5ErkJggg=="
}
},
"logoImage": {
"kind": "upload",
"file": {
"filename": "example-logo.svg",
"base64": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiM0NTVFRUYiLz48L3N2Zz4="
}
}
},
"benefits": {
"benefitAlpha": "Member news"
}
}
To create with the built-in background and no default logo, use this separate request instead of an upload object for those slots:
{
"requestId": "membership-create-no-logo-001",
"channelCode": "selected-channel-code",
"title": "Community membership",
"access": {
"installedPAppIds": [
17,
24
],
"expectedOptionsRevision": "0000000000000000000000000000000000000000000000000000000000000000"
},
"dryRun": true,
"card": {
"backgroundImage": "reset-default",
"logoImage": "remove"
}
}
Removal clears the new card's logo reference; it does not delete a file. Explicit reset-default uses the same built-in background as omission. Neither action adds a follow-up membership update: the intended values belong to the original single creation POST.
Recovery and destination
Use task_status/task_resume with taskType:"membership-create" and the returned
taskId. Status reads the journal. Status/resume never issue image grants, upload or
POST, including prepared tasks with already uploaded files. A prepared task can report
MEMBERSHIP_CREATE_NOT_DISPATCHED; only the identical original create input, including
original image bytes, can continue unattempted effects after rechecking. Acknowledged
uploads are retained; uncertain grant-sent/upload-sent stages are never replayed. A sent task with no validated receipt ID returns
MEMBERSHIP_CREATE_ID_UNRESOLVED. Preserve it and inspect the console; never guess by
title or retry with a different requestId. There is no automatic membership deletion
or rollback. A known receipt permits only that record's fresh read and comparison;
matching recovery returns MEMBERSHIP_CREATE_RECONCILED. Completed replay is historical.
The single canonical console link opens the creation form for a plan, the created membership editor after verified completion, or the membership list when no verified created identity is available. It uses the centrally configured console origin. Demo has no real console destination. Sequential checks do not provide atomic compare-and-swap, causal ownership, rendered appearance or visitor-access proof.
Inputs
| Field | Required here | Contract |
|---|---|---|
title |
Yes | string; min length 4; max length 50 |
card |
No | object |
card.designSchema |
No | A, B |
card.overlay |
No | one of: NONE, SOLID, GRADIENT (by type) |
card.overlay.primary |
Yes | When card.overlay.type is SOLID: string; pattern ^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$ |
card.overlay.primary |
Yes | When card.overlay.type is GRADIENT: string; pattern ^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$ |
card.overlay.secondary |
Yes | When card.overlay.type is GRADIENT: string; pattern ^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$ |
card.backgroundImage |
No | reset-default or one of: local-file, more-parts, complete, cancel (by kind) |
card.backgroundImage.filename |
Yes | When card.backgroundImage.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. |
card.backgroundImage.byteLength |
Yes | When card.backgroundImage.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat). |
card.backgroundImage.sha256 |
No | When card.backgroundImage.kind is local-file: string; pattern ^[0-9a-f]{64}$ |
card.backgroundImage.uploadId |
Yes | When card.backgroundImage.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
card.backgroundImage.fromPart |
Yes | When card.backgroundImage.kind is more-parts: integer; minimum 1; maximum 10000 |
card.backgroundImage.uploadId |
Yes | When card.backgroundImage.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
card.backgroundImage.parts |
Yes | When card.backgroundImage.kind is complete: array; min items 1; max items 10000 |
card.backgroundImage.parts[].partNumber |
Yes | When card.backgroundImage.kind is complete: integer; minimum 1; maximum 10000 |
card.backgroundImage.parts[].etag |
Yes | When card.backgroundImage.kind is complete: string; min length 1; max length 200 |
card.backgroundImage.measured |
No | When card.backgroundImage.kind is complete: object; What the video measures on this computer, read with measureCommand. |
card.backgroundImage.measured.durationSeconds |
Yes | When card.backgroundImage.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
card.backgroundImage.measured.frameRate |
Yes | When card.backgroundImage.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
card.backgroundImage.measured.width |
Yes | When card.backgroundImage.kind is complete: integer; maximum 100000; greater than 0 |
card.backgroundImage.measured.height |
Yes | When card.backgroundImage.kind is complete: integer; maximum 100000; greater than 0 |
card.backgroundImage.measured.frameRateMode |
No | When card.backgroundImage.kind is complete: CFR, VFR |
card.backgroundImage.measured.minFrameRate |
No | When card.backgroundImage.kind is complete: number; maximum 1000; greater than 0 |
card.backgroundImage.measured.maxFrameRate |
No | When card.backgroundImage.kind is complete: number; maximum 1000; greater than 0 |
card.backgroundImage.uploadId |
Yes | When card.backgroundImage.kind is cancel: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
card.logoImage |
No | remove or one of: local-file, more-parts, complete, cancel (by kind) |
card.logoImage.filename |
Yes | When card.logoImage.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. |
card.logoImage.byteLength |
Yes | When card.logoImage.kind is local-file: integer; greater than 0; The exact file size in bytes (for example from wc -c < FILE or stat). |
card.logoImage.sha256 |
No | When card.logoImage.kind is local-file: string; pattern ^[0-9a-f]{64}$ |
card.logoImage.uploadId |
Yes | When card.logoImage.kind is more-parts: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
card.logoImage.fromPart |
Yes | When card.logoImage.kind is more-parts: integer; minimum 1; maximum 10000 |
card.logoImage.uploadId |
Yes | When card.logoImage.kind is complete: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
card.logoImage.parts |
Yes | When card.logoImage.kind is complete: array; min items 1; max items 10000 |
card.logoImage.parts[].partNumber |
Yes | When card.logoImage.kind is complete: integer; minimum 1; maximum 10000 |
card.logoImage.parts[].etag |
Yes | When card.logoImage.kind is complete: string; min length 1; max length 200 |
card.logoImage.measured |
No | When card.logoImage.kind is complete: object; What the video measures on this computer, read with measureCommand. |
card.logoImage.measured.durationSeconds |
Yes | When card.logoImage.kind is complete: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
card.logoImage.measured.frameRate |
Yes | When card.logoImage.kind is complete: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
card.logoImage.measured.width |
Yes | When card.logoImage.kind is complete: integer; maximum 100000; greater than 0 |
card.logoImage.measured.height |
Yes | When card.logoImage.kind is complete: integer; maximum 100000; greater than 0 |
card.logoImage.measured.frameRateMode |
No | When card.logoImage.kind is complete: CFR, VFR |
card.logoImage.measured.minFrameRate |
No | When card.logoImage.kind is complete: number; maximum 1000; greater than 0 |
card.logoImage.measured.maxFrameRate |
No | When card.logoImage.kind is complete: number; maximum 1000; greater than 0 |
card.logoImage.uploadId |
Yes | When card.logoImage.kind is cancel: string; pattern ^upl_[A-Za-z0-9_-]{22}$ |
benefits |
No | object |
benefits.benefitAlpha |
No | string; max length 50 |
benefits.benefitBravo |
No | string; max length 50 |
benefits.benefitCharlie |
No | string; max length 50 |
benefits.benefitDelta |
No | string; max length 50 |
benefits.benefitEcho |
No | string; max length 50 |
access |
Yes | object; The complete intended installed-feature set and current options revision from memberships_get view.kind create-options. Empty requires confirmEmpty:true. |
access.installedPAppIds |
Yes | array; items: integer; greater than 0; max items 10000; The complete desired installed-feature ID set, not additions only. Omit access to preserve all associations. Copy IDs from memberships_get detail with includeAccessOptions:true. |
access.confirmEmpty |
No | true |
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. |