Member subscriptions
Read the member's memberships and change the renewal preference on an existing Pricing relationship. Exact API subscription names are retained: these routes use a Pricing ID, while membership means the member's existing relationship. Selecting a new Pricing for purchase belongs to checkout.
| Task | Operation |
|---|---|
| Read member memberships in the resource | List subscriptions |
| Turn existing renewal preference on/off | Change renewal |
Read memberships in this resource
GET /api/v1/user/subscriptions
Read Pricing objects with this member’s membership dates and renewal flags added. This is not a public catalog listing or a list filtered to active/unexpired records: the query selects existing member relationships joined to their Pricing and Plan.
Before you call
Establish member identity, or its Firebase identity, in a valid resource. A relationship must exist to produce an item. Public Pricing availability alone is not membership.
Request
GET with no body or request Content-Type. Requires user role/inherited permission and active/unlocked member context. Supply sample configuration.
| Name | Location | Type | Requirement / default | Meaning |
|---|---|---|---|---|
resource | header | string | member request context | Public resource key for ordinary member-session lookup and resource-aware response. The controller requires user, not an explicit require-resource guard; provide valid resource context. |
token | header | string | member context | Existing Wallkit member token; user role or inherited permission. |
firebase-token | header | string | configured Firebase context | Existing Firebase ID token alongside Wallkit token; see credentials. |
page | query | integer | optional; examples pass 1 | Page selection; no explicit omitted-page default in the paginator call. |
limit | query | integer | optional; configured results_on_page | Page size; not the controller's fixed 10 default. No universal maximum specified. |
filter | query | object / JSON string | optional | Model-field criteria across UserPlanRelationships, Subscriptions and Plans. Bracket/JSON transport; text substring and numeric comparisons/arrays as supported by the common filter. |
order / by | query | string | optional | order overrides by; default UserPlanRelationships.created_at. |
sort | query | string | optional, DESC | Ordering direction; ASC/DESC for ordinary use. |
Pass limit explicitly: the paginator uses configured results_on_page (documented default: 10), while the omitted-limit response field uses the controller default 10; changed configuration can make those defaults differ.
The query is restricted to member ID and Plan resource, grouped by membership ID. Several membership records can therefore produce repeated Pricing IDs. The returned id is the Pricing ID, not the grouping membership ID. Filter errors do not have this action's explicit validity/error gate; do not assume the transaction-list error handling applies here.
Result
HTTP 200 JSON items[] plus paginator fields. Each item contains general Pricing, including its selected nested catalog fields, plus:
| Additional field | Type / presence | Meaning |
|---|---|---|
subscription_start_date | stored timestamp string / nullable | Membership creation time. |
subscription_end_date | stored timestamp string / nullable | Membership expiration. |
subscription_updated_date | stored timestamp string / nullable | Membership update time. |
autorenew | boolean | Member's current renewal preference. |
is_trial | boolean | Membership trial flag. |
is_team_subscription | boolean | Team membership flag. |
No timezone guarantee or content entitlement is implied. Empty items is an ordinary no-matching-record result.
Example: read Pricing 2001's relationship
The member already has a relationship to Pricing 2001; dates and flags explain its recorded state. The three requests are equivalent.
cURL
curl "${WALLKIT_API_BASE}/api/v1/user/subscriptions?page=1&limit=10" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/subscriptions", process.env.WALLKIT_API_BASE);
url.searchParams.set("page", "1");
url.searchParams.set("limit", "10");
const response = await fetch(url, { method: "GET", headers: {
resource: process.env.RESOURCE_KEY, token: process.env.USER_TOKEN
}});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urlencode, urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/user/subscriptions") + "?" + urlencode({'page': '1', 'limit': '10'})
headers = {"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_TOKEN"]}
request = Request(url, headers=headers, method="GET")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt (catalog fields/paginator omitted):
{"items":[{"id":2001,"plan_id":1001,"title":"Monthly","subscription_start_date":"2026-09-01 10:00:00","subscription_end_date":"2026-10-01 10:00:00","subscription_updated_date":"2026-09-01 10:00:00","autorenew":true,"is_trial":false,"is_team_subscription":false}]}
Use Pricing 2001 for renewal preference requests, not an invented membership ID. Dates are recorded values, not proof that an article is currently allowed.
Consequential alternate: no matching memberships
HTTP 200 excerpt:
{"items":[]}
Show an empty account state and check selected filters/resource before offering a purchase. The result is not an authentication error or a complete cross-resource membership history.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Missing/invalid member or inactive/locked/suspended context. | Check the existing session/resource; ask the administrator about restrictions. |
| 200 | items: [] | No matching joined relationships in the selected member/resource/filter scope. | Check scope/filters; read the catalog only if a new Pricing selection is needed. |
No handler-specific resource/filter failure status is promised beyond these visible branches. Supply a valid resource and supported field criteria; do not replace missing context with an empty-account assumption.
Next task
Use renewal change for an existing relationship, or select a new Pricing through checkout. For content serving, obtain the separate access decision.
Change renewal on an existing relationship
POST /api/v1/user/subscriptions/{id}
Set the existing member/Pricing relationship's autorenew preference. This does not switch the member to another Pricing, perform an immediate charge, cancel access, refund a payment or delete the membership. The profile PUT and profile DELETE retain their distinct contracts.
Before you call
Requires active member context and user-role/inherited permission. The numeric path ID must identify an existing Pricing with a relationship for this member. Enabling renewal requires that Pricing's is_allowed_autorenew. The lookup checks member/Pricing IDs without a separate header-resource match; retain the intended member/resource/Pricing association in your integration.
The helper saves the renewal flag, invalidates membership caches and requests renewal events/conditional Campaign Monitor sync. It does not check the save boolean before the handler returns success. Read the returned state; HTTP 200 does not promise future successful renewal or event delivery. No membership deletion/payment-provider call is performed by this action itself.
Request
POST JSON object; null/omitted autorenew does not enter the update branch. Send a JSON boolean. The source's BooleanFilter treats false/empty and trimmed "false" as false, other truthy sanitized values as true; this is not strict JSON-boolean validation.
| Name | Location | Type | Requirement | Meaning |
|---|---|---|---|---|
resource | header | string | member request context | Public resource key for ordinary member-session lookup and resource-aware response. The controller requires user, not an explicit require-resource guard; provide valid resource context. |
token | header | string | member context | Existing Wallkit member token; user role or inherited permission. |
firebase-token | header | string | configured Firebase context | Existing Firebase ID token alongside Wallkit token; see credentials. |
id | path | integer | required, digits | Existing Pricing ID from the membership list. |
autorenew | body | boolean recommended | needed for change | true enables; false disables. Null/omitted yields HTTP 400 user projection without this update. |
Result
HTTP 200 JSON top-level resource-aware user plus subscriptions and teams. Use the resource-aware user fields and profile user projections, matching the existing renewal profile projection. No subscriptions_history, assigned_tickets or last_action addition. Membership projection there does not expose the relationship ID used internally.
Example: stop future renewal preference
Pricing 2001 already belongs to this member. The request changes the preference, not current access/expiry.
cURL
curl "${WALLKIT_API_BASE}/api/v1/user/subscriptions/2001" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"autorenew":false}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/subscriptions/2001", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify({"autorenew":false})
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/user/subscriptions/2001")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"Content-Type": "application/json"
}
body = {'autorenew': False}
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt:
{"id":7001,"subscriptions":[{"id":2001,"plan_id":1001,"autorenew":false}],"teams":[]}
The full resource-aware user projection contains identity/profile fields. Inspect the matching returned membership flag. Existing expiry is not cancelled by this request.
Consequential alternate: selected Pricing is not renewable
HTTP 400 response excerpt:
{"error":"incorrect_subscription","error_description":"This subscription is not renewable","req_guid":"example-request","id":7001,"subscriptions":[{"id":2001,"plan_id":1001,"autorenew":false}],"teams":[]}
This is the helper's enabling failure, followed by the current user projection. Do not treat the included user object as a successful update. Offer renewal only for a configured renewable Pricing.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Invalid identity/account/resource restrictions. | Repair existing session/context or ask administrator about restrictions. |
| 406 | incorrect_data | Missing JSON object. | Send an autorenew JSON object. |
| 404 | incorrect_subscription, Subscription not found | Pricing missing or no current member relationship. | Read existing memberships; select their Pricing ID. |
| 400 | incorrect_user / incorrect_subscription / exception plus user fields | Renewal helper throws. | Inspect description/current state; check eligibility/configuration before another change. |
| 400 | User projection, no handler error | autorenew omitted/null. | Send true/false if a change is intended; this response did not update the flag. |
Also registered as
POST /api/v1/user/subscription/{id} is the singular alias, with the same handler, inputs and result. It shares this canonical operation; it is not an additional renewal flow.
Next task
Read the returned account state and content decision if serving an article. For a new purchase, use checkout; turning on renewal is not a new purchase.