Member membership and purchase history
This option reads the original member-history tabs; orders and refunds are separate PAY observations with their own permissions and prerequisites.
Select within the same member
Use members_list to select an authorized channel and numeric profileId. OWNER or a delegate with MANAGE_MEMBERS is required. A member profile ID is distinct from an account ID, distinctId, membership id/mid and product ID. Omit include when selecting history; the reader does not add contact or sign-up answers. No monetization permission or PAY issuer configuration is required for these core GETs.
{"channelCode":"selected-channel-code","profileId":123,"membershipHistory":{"kind":"memberships"}}
Choose a product from this result's membershipHistory.memberships[].subscriptionProducts. Use its parent's mid and its own id as a canonical numeric string. productReads supplies same-channel/member recipes; choose only the one needed for the user's purpose. The reader confirms the parent/product again before fetching purchases. The membership list has no page, search or status filter and is never silently truncated.
{"channelCode":"selected-channel-code","profileId":123,"membershipHistory":{"kind":"purchases","membershipMid":"observed-membership-mid","subscriptionProductId":"31","page":2,"limit":30}}
Purchases defaults to page 1 / limit 15 when omitted. page is a positive integer up to 1,000,000; limit is 15/30/50/100. Follow a non-null membershipHistory.purchases.nextPage only when another page is needed. One page, including an empty out-of-range page, does not prove the whole purchase inventory was read. No PAY record or financial action is requested by following this recipe.
Projection and bounds
| Object | Retained observations and constraints |
|---|---|
| member | Positive safe numeric id; bounded distinctId, nickname, imageSrc, registration and ban state, country code; optional accountProvider string; bannedAt may be omitted, null or a string. registeredAt may be null. isBanned is independent of the date. No contact, sign-up answers or credentials. |
| membership | Positive safe numeric id, mid, title and subscriptionProducts. At most 1,000 memberships; unique ids and mids. |
| subscription product | Positive safe numeric id, title, status, subscriptionType, nonnegative safe integer expiryPeriodInDays and firstPurchasedAt. Unique product ids within a membership; at most 1,000 products per membership and 5,000 overall. |
| purchase row | Positive safe numeric id, oid, status, subscriptionType, startAt, expiresAt and price. At most 100 rows and unique row ids on the requested page. |
| price | currency is a nonempty string up to 32 characters; amount is a decimal string up to 256 characters, optionally negative, without exponent notation or currency conversion. No inferred zero or rounded numeric replacement. |
| pagination | currentPage and limit must equal the request. totalEntries is a nonnegative safe integer; row count must equal min(limit, max(0, totalEntries - (page - 1) * limit)). nextPage is null at or beyond the last page. |
Titles, nickname and imageSrc are bounded to 4,096 characters. Status/type strings are nonempty and at most 128 characters, without a closed enum or translated meaning. History date strings are at most 128 characters; empty strings are allowed by this shape, null is not. The profile's registeredAt and optional bannedAt separately permit null; this does not make product/purchase dates nullable. These are parsing rules, not proof of backend null/date compatibility. A field named imageSrc is an untrusted reference, not a request to fetch an image.
Purchase subscriptionProductTitle must equal the observed parent product title, and every purchase row's subscriptionType must equal that product's type. Preserve a mismatch as an error; do not rewrite historical values to pass. Finite/infinite periods, UPCOMING and other raw states are not converted into payment, access, refund or cancellation conclusions.
Read sequence and exact console destination
All new profile/history requests use the channel's core authorization and a 2 MiB response-body ceiling, including ignored extra fields. They require the exact 200/data envelope and recheck the credential on failures. The workflow reads profile and memberships, verifies the selected purchase target if any, reads the selected purchase projection twice, then rereads memberships/profile and compares channel/actor/permission/credential context. Array order and retained values participate in equality; unknown stripped fields do not. This is not an atomic snapshot.
The list destination opens /channels/{channelCode}/members/membership-users/{profileId}/membership-history. The purchase destination appends /{membershipMid}/{subscriptionProductId}?page={page}&limit={limit}; it retains the requested page and limit. The shared finalizer checks the returned profile and parent/product, selection and pagination, allowing exactly those two purchase query fields. It supplies at most one matching destination and removes nested URLs. An address proves neither browser access nor rendered-console equivalence; demo has no live address.
Errors and next action
| Code | Handling |
|---|---|
| INVALID_INPUT | Correct the strict selection, identifiers, page/limit or incompatible nonempty include. Do not assume the exported shape expresses every refinement. |
| MEMBER_HISTORY_PROFILE_UNVERIFIED | Resolve the selected profile identity/shape before interpreting history. |
| MEMBER_HISTORY_UNVERIFIED | Missing, malformed, duplicate or oversized membership history is not an empty success. |
| MEMBER_HISTORY_TARGET_NOT_FOUND | Read this member's memberships and select an observed mid/product; no purchase lookup occurs for an absent target. |
| MEMBER_PURCHASE_HISTORY_UNVERIFIED | Purchase envelope/fields failed validation; no partial page is returned. |
| MEMBER_PURCHASE_PAGE_MISMATCH | Requested page/limit, exact row count or unique row ids do not agree. |
| MEMBER_PURCHASE_TARGET_MISMATCH | Product title or subscription type differs from the selected history product. Retain the mismatch; historical compatibility requires evidence. |
| MEMBER_HISTORY_CHANGED / SCREEN_CONTEXT_CHANGED | Discard the observation and reread current authorized state. Do not merge rows from different observations. |
| SCREEN_ROLE_DENIED / CHANNEL_CONTEXT_MISMATCH / credential or upstream errors | Resolve the reported access, target or service prerequisite without switching accounts or widening credentials. |
Success is MEMBER_MEMBERSHIP_HISTORY with writesPerformed:false. confirmed:true covers the checked stored observations only; paymentVerified, effectiveAccessVerified and financialActionCompleted remain false. No member edit, refund, recurring cancellation, CSV export, journal or task-resume operation is included.
Nullable-profile compatibility
An absent or null ban date does not infer isBanned. Malformed dates/types, wrong profile identity and inconsistent history still fail without partial results. Do not invent a date or perform moderation as a read workaround.