Member profile management
Read the current profile before choosing a change. Use POST password only when the resource password is unset. PUT password changes credentials, while PUT email requests confirmation. Linking or unlinking a social account changes global identity fields. Renewal flags and membership removal act on existing memberships. Suspension and profile deletion affect the selected resource; each operation explains any conditional effects on global records or the provider.
Member context
These operations use the resolved member. User ACL and shared resource, session and account checks apply. Supply the Wallkit session in token and the intended resource in resource, except where an operation explicitly permits a resource-less branch. Firebase-enabled members also need the matching ID token in firebase-token; omit that header for an ordinary integration.
The examples use Firebase-enabled member context and synthetic inputs. See credentials and sample runtimes. Read each operation’s scope before changing global or resource records.
Operations
| Task | Method / path |
|---|---|
| Remove Wallkit refresh records | POST /api/v1/cleanup-refresh |
| Read the current member profile | GET /api/v1/user |
| Update profile fields and tags | PUT /api/v1/user |
| Set an initial resource password | POST /api/v1/user/password |
| Change an existing resource password | PUT /api/v1/user/password |
| Check whether a password is stored | GET /api/v1/user/has-password |
| Request an email change | PUT /api/v1/user/email |
| Link a provider identity | PUT /api/v1/user/social |
| Unlink a provider identity | POST /api/v1/user/social/delete |
| Suspend the member in this resource | POST /api/v1/user/suspend |
| Remove the member profile from this resource | DELETE /api/v1/user/profile |
| Detach a subscription membership | DELETE /api/v1/user/subscriptions/{id} |
| Change membership renewal flags | PUT /api/v1/user/subscriptions/{id} |
| Replace the user photo | POST /api/v1/user/photo |
| Understand the unavailable history-write route | POST /api/v1/user/history |
| List the member’s tags | GET /api/v1/user/tags |
| Read subscription-change history | GET /api/v1/user/plans/history |
Remove Wallkit refresh records
POST /api/v1/cleanup-refresh
Remove all stored Wallkit refresh-token records for this user, across resource contexts. This writes refresh records; it does not revoke existing Wallkit sessions or Firebase tokens.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| msg | object | Message envelope, not a top-level result. |
| msg.result | boolean | true after cleanup is invoked; no removed-record count. |
| msg.msg | string | Fixed cleanup acknowledgement. |
Example
Remove this user’s Wallkit refresh records. See member context for configuration and sample conventions.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/cleanup-refresh" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/cleanup-refresh", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "POST",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/cleanup-refresh')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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:
{
"msg": {
"result": true,
"msg": "Refresh tokens cleanup successfully"
}
}
Alternate result
A missing resolved user stops the request with HTTP 404:
{
"error": "user_not_exist",
"error_description": "User does not exist"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_not_exist | Missing user. | Restore valid member context; cleanup is not performed after this missing-user error. |
Shared context errors apply before the action.
Next task
Sign in through the selected identity flow when new credentials are needed.
Read the current member profile
GET /api/v1/user
Read the resolved user and, with a resource, their subscriptions, teams, subscription history and assigned tickets. This route uses the current-member projection; it does not issue credentials or authorize content.
Before you call
Resolved member is required. Resource is optional at the action level; this example selects it. Firebase-enabled members need both matching token headers. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | optional | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
Example
Read member 42 with this resource’s relationship collections. See member context for configuration and sample conventions.
cURL
curl -X GET "${WALLKIT_API_BASE}/api/v1/user" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "GET",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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:
{
"id": 42,
"email": "reader@example.com",
"subscriptions": [],
"teams": [],
"subscriptions_history": [],
"assigned_tickets": []
}
Alternate result
Without resource context, the user-only branch returns the base user projection; relationship additions are absent. HTTP 200 excerpt:
{
"id": 42,
"email": "reader@example.com"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| context-dependent | Shared identity/context errors | No resolved member. | Restore the matching identity credentials. |
Shared context errors apply before the action.
Next task
Use the member’s resource context for a content-access decision.
Update profile fields and tags
PUT /api/v1/user
Update global profile text and selected resource extra/language. Tag replacement removes this user’s tag relationships across all resources before adding selected-resource tags; [] clears them. Resource extra/language writes can occur before later tag/global validation failure. This is not an atomic profile update. Successful completion records profile/optional marketing events.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| nick_name | body | string / null | optional; presence-sensitive | Nickname normalization/uniqueness validation, filtered length3–64; null does not guarantee clearing. |
| first_name | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| last_name | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| country | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| city | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| state | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| address | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| ip | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| company | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| job | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| zip | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| phone | body | string | optional; isset | Trimmed profile text; omission/null leaves it unchanged; no action-level length limit here. |
| extra | body | object | optional | Selected resource relationship extra; helper merges its supplied keys, not this separate extra-data group API. |
| language_id | body | integer-filtered input | optional; nonempty | Existing language ID. No clearing behavior for empty/null. |
| language_slug | body | string | optional; nonempty | Existing language slug; processed after language_id and overrides it when both present. |
| tags | body | array | optional | Replacement tag list; [] removes all user tag relationships across resources. Each item nonempty length3–25, not object/array. Non-array input is ignored. |
| body | unsupported non-null field | do not send | Use the separate email-change operation. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
| tags | array of tag slug strings; conditional | Included only when tags input is an array; current resource/global read scope differs from all-resource replacement. |
Example
Change the member’s first_name to Alex. See member context for configuration and sample conventions.
cURL
curl -X PUT "${WALLKIT_API_BASE}/api/v1/user" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"first_name":"Alex"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "PUT",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"first_name":"Alex"})
});
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')
body = {'first_name': 'Alex'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, method='PUT')
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": 42,
"email": "reader@example.com",
"subscriptions": [],
"teams": [],
"first_name": "Alex",
"assigned_tickets": []
}
Alternate result
A non-null email field in the profile body is rejected. HTTP 409 excerpt:
{
"error": "incorrect_request",
"error_description": "Use api: /user/email for change email"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data | Missing/non-JSON body. | Send the documented JSON object. |
| 409 | incorrect_request | Email sent in this profile body. | Use PUT /api/v1/user/email. |
| 409 | nickname validation code / invalid_language_id / invalid_language_slug / invalid_tags_format / invalid_tag | Invalid nickname, language or tag. | Correct the named input; earlier resource writes may already exist. |
| 406 / 422 | update_user_fail / update_users_fail | Tag/helper/user-save failure. | Inspect current profile state before repeating the write. |
Shared context errors apply before the action.
Next task
Set an initial resource password
POST /api/v1/user/password
Set a password only when the current resource relationship has no password. This can update/create provider credentials and resource password; if the global password is empty, it also sets it. It deactivates reset records after the operation. Provider and local changes are not an atomic unit. The resource-password update also writes default confirmation=false, language=null and settings=null; Firebase UID can become null when its inactive-service branch supplies no UID. Existing extra is preserved. Do not assume only the password changes. result:true does not independently verify the helper save result.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| password | body | string | required | Length6–40, trimmed for storage. |
| password_confirm | body | string | required matching confirmation | Must match password. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| result | boolean | true when this action reaches its completion response; read its persistence/provider limitations. |
Example
Set the first resource password with matching confirmation. See member context for configuration and sample conventions.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/user/password" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"password":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE","password_confirm":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/password", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "POST",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"password":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE","password_confirm":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE"})
});
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/password')
body = {'password': 'EXAMPLE_NEW_PASSWORD_DO_NOT_USE', 'password_confirm': 'EXAMPLE_NEW_PASSWORD_DO_NOT_USE'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, 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:
{
"result": true
}
Alternate result
A resource password already exists, so the initial-set operation rejects it. HTTP 422 excerpt:
{
"error": "user_resource_password_exist",
"error_description": "User resource relation password exist"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data | Missing/non-JSON body. | Send the documented JSON object. |
| 404 | user_resource_not_exist | No resource relationship. | Establish this resource membership first. |
| 422 | user_resource_password_exist / invalid_password | Existing password or invalid length/confirmation. | Use password-update for an existing password; otherwise correct length/confirmation. |
| 406 | set_password_fail | Provider/local orchestration exception. | Inspect credential state with support before repeating. |
Shared context errors apply before the action.
Next task
Use ordinary sign-in for this resource when its identity mode supports that flow.
Change an existing resource password
PUT /api/v1/user/password
Change the selected resource password, optionally verifying its old password. Configured Firebase credentials may update; global password changes only in root/admin-context branches. It deactivates reset records. No all-session logout or cross-system atomicity guarantee. The resource-password update also writes default confirmation=false, language=null and settings=null; Firebase UID can become null when its inactive-service branch supplies no UID. Existing extra is preserved. Do not assume only the password changes. result:true does not independently verify the helper save result.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| old_password | body | string | required only when resource password nonempty | Verified against the applicable stored password. |
| password | body | string | optional alternative | When nonempty, length6–40 and password_confirm match; copied to new_password. |
| password_confirm | body | string | required with nonempty password | Confirmation for password branch. |
| new_password | body | string | required when password branch unused | Length6–40; this alternative has no confirmation field check. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| result | boolean | true when this action reaches its completion response; read its persistence/provider limitations. |
Example
Change the existing resource password using its old password. See member context for configuration and sample conventions.
cURL
curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/password" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"old_password":"EXAMPLE_OLD_PASSWORD_DO_NOT_USE","password":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE","password_confirm":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/password", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "PUT",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"old_password":"EXAMPLE_OLD_PASSWORD_DO_NOT_USE","password":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE","password_confirm":"EXAMPLE_NEW_PASSWORD_DO_NOT_USE"})
});
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/password')
body = {'old_password': 'EXAMPLE_OLD_PASSWORD_DO_NOT_USE', 'password': 'EXAMPLE_NEW_PASSWORD_DO_NOT_USE', 'password_confirm': 'EXAMPLE_NEW_PASSWORD_DO_NOT_USE'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, method='PUT')
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:
{
"result": true
}
Alternate result
The supplied old password does not match the required resource credential. HTTP 406 excerpt:
{
"error": "change_password_fail",
"error_description": "Wrong current password"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data | Missing/non-JSON body. | Send the documented JSON object. |
| 422 | invalid_<field> | Missing/invalid password input. | Correct required old/new length/confirmation inputs. |
| 406 | change_password_fail | Incorrect old password or missing looked-up user. | Correct current password or contact support. |
| 406 | save_password_fail | Provider/local save orchestration failure. | Inspect password state before repeating. |
Shared context errors apply before the action.
Next task
Use the appropriate identity flow with the changed password.
Check whether a password is stored
GET /api/v1/user/has-password
Check whether either the resource or global user has a stored password. The positive result does not verify a submitted password. A missing resource relationship or absence of both stored passwords stops the request with HTTP 404.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| status | boolean | true on the final positive response path; there is no source-defined status:false branch. |
Example
Inspect whether this member has a stored resource or global password. See member context for configuration and sample conventions.
cURL
curl -X GET "${WALLKIT_API_BASE}/api/v1/user/has-password" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/has-password", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "GET",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/has-password')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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:
{
"status": true
}
Alternate result
When both stored passwords are empty, the request stops with HTTP 404. Handle this error instead of expecting status:false:
{
"error": "user_password_not_set",
"error_description": "User password does not set"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_resource_not_exist / user_password_not_set | Missing resource relationship or both stored passwords empty. | Restore the resource relationship if missing. If both passwords are empty, choose the authorized initial-password flow. |
Shared context errors apply before the action.
Next task
Use initial-password setting when the account flow calls for it.
Request an email change
PUT /api/v1/user/email
Create a confirmation hash and request the configured email-change event. This writes a confirmation record and can enqueue mail; it does not immediately change the email or prove delivery.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body. Email/social operations also accept the applicable form fields; these examples choose JSON.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| body | string | required | Valid email, max60; trimmed/lowercased; cannot equal current email or another global user’s email. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| result | boolean | true after confirmation record/event preparation; email change remains unconfirmed. |
Example
Request confirmation for new-reader@example.com. See member context for configuration and sample conventions.
cURL
curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/email" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"email":"new-reader@example.com"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/email", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "PUT",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"email":"new-reader@example.com"})
});
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/email')
body = {'email': 'new-reader@example.com'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, method='PUT')
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:
{
"result": true
}
Alternate result
The email validator rejects an address already used by any global user before later same-address checks. It also rejects more than 100 user confirmation records, more than 50 user/resource records, or at least 5 user/resource records within the preceding hour. HTTP 422 excerpt:
{
"error": "invalid_email",
"error_description": "E-mail address already in use."
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 422 | invalid_email | Missing/invalid format or length. | Correct email format/length. |
| 422 | invalid_email; E-mail address already in use. | Address belongs to an existing global user. | Choose the intended unused address. |
| 422 | invalid_email; Requests limit exceeded. | Confirmation-record total or preceding-hour count exceeded. | Wait when only the hourly limit applies; contact support for global/resource totals, which a one-hour wait may not clear. |
| 409 | invalid_new_email | Same email or globally used email. | Choose the intended unused address. |
| 409 | error_create_hash | Confirmation hash save failed. | Contact support; delivery and email change are not established. |
Shared context errors apply before the action.
Next task
Link a provider identity
PUT /api/v1/user/social
Verify a Google/Facebook identity and store its global provider ID on the user. Calls the configured provider and saves global identity fields. A 201 can also be returned unchanged when id is missing or method is unrecognized; status alone does not prove linking.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body. Email/social operations also accept the applicable form fields; these examples choose JSON.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| method | body | string | required | google or facebook for a useful implemented branch; no inclusion validator here. |
| data | body | object | optional | When supplied, id/access_token are read from this nested body instead. |
| id | body or data | string | needed for actual linking | Provider user ID, unique across global Google/Facebook-linked users. |
| access_token | body or data | string | needed for provider verification | Provider access token matching id; not a Wallkit/Firebase token. |
Result
HTTP 201 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
Example
Link the matching Google provider identity. See member context for configuration and sample conventions.
cURL
curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/social" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"method":"google","id":"EXAMPLE_GOOGLE_USER_ID","access_token":"EXAMPLE_PROVIDER_ACCESS_TOKEN"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/social", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "PUT",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"method":"google","id":"EXAMPLE_GOOGLE_USER_ID","access_token":"EXAMPLE_PROVIDER_ACCESS_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/social')
body = {'method': 'google', 'id': 'EXAMPLE_GOOGLE_USER_ID', 'access_token': 'EXAMPLE_PROVIDER_ACCESS_TOKEN'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, method='PUT')
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 201 response excerpt:
{
"id": 42,
"google_id": "EXAMPLE_GOOGLE_USER_ID",
"subscriptions": []
}
Alternate result
Missing outer method is rejected before linking. HTTP 400 excerpt:
{
"error": "incorrect_data",
"error_description": "Body must have method"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data / account_error | Missing method or provider ID already used. | Supply the implemented method or choose the correct existing linked account. |
| 406 | invalid_social_id | Verified provider ID differs. | Supply the access token and ID from the same provider identity. |
| 409 | error_social | Provider/save orchestration exception. | Check existing configured provider integration with support. |
Shared context errors apply before the action.
Next task
Read profile/provider fields to interpret the resulting link.
Unlink a provider identity
POST /api/v1/user/social/delete
Clear the user’s global Google or Facebook ID. This affects the shared Wallkit user across resources; it does not delete the external provider account or revoke its tokens. An unrecognized method can still return HTTP 201 unchanged.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body. Email/social operations also accept the applicable form fields; these examples choose JSON.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| method | body | string | required | google or facebook selects the global field to clear. No inclusion validator. |
Result
HTTP 201 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
Example
Clear the global Google identity field. See member context for configuration and sample conventions.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/user/social/delete" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"method":"google"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/social/delete", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "POST",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
body: JSON.stringify({"method":"google"})
});
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/social/delete')
body = {'method': 'google'}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, 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 201 response excerpt:
{
"id": 42,
"google_id": null,
"subscriptions": []
}
Alternate result
Missing method cannot select the link to remove. HTTP 400 excerpt:
{
"error": "incorrect_data",
"error_description": "Body must have method"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data | Missing method. | Choose the intended implemented provider. |
| 404 | user_not_exists | User not found in resource lookup. | Restore the intended member/resource context. |
| 409 | error_delete_social | Save orchestration failed. | Read current identity state before repeating. |
Shared context errors apply before the action.
Next task
Suspend the member in this resource
POST /api/v1/user/suspend
Set this resource relationship’s suspended flag and emit suspension/optional marketing events. This is resource-scoped suspension; it does not set the global user inactive. The returned resource-aware active value reflects the suspended relationship.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
Example
Suspend the member’s relationship to this resource. See member context for configuration and sample conventions.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/user/suspend" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/suspend", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "POST",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/suspend')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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": 42,
"email": "reader@example.com",
"subscriptions": [],
"teams": [],
"active": false
}
Alternate result
Shared initialization rejects a missing/incorrect resource context; suspension is not an action-defined no-op. HTTP 404 excerpt:
{
"error": "resource_not_exists",
"error_description": "Incorrect resource key"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | resource_not_exists | Missing/incorrect resource context. | Supply the intended resource public key before selecting suspension. |
Shared context errors apply before the action.
Next task
Handle subsequent member access as restricted and resolve reactivation with the integration owner.
Remove the member profile from this resource
DELETE /api/v1/user/profile
Remove the resource relationship and its plans (after history), sessions, extra-data rows and selected local payment-customer records. Provider authentication/Firestore deletion is attempted conditionally and failures are caught; shared Firebase UIDs skip provider deletion. If no other resource relationship exists, selected global profile fields are cleared and the global record deactivated, rather than deleted. History/other global records may remain; success does not promise all-data erasure, provider cancellation or refunds. When auto_clear_content_views is enabled on the deleted plan’s resource, membership deletion can also remove this user’s AccessContent view records without a resource filter; this can affect view history across resources.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| status | string | success when the deletion orchestrator returns and the event is emitted; no per-system deletion receipt. |
Example
Remove the member’s profile from this resource. See member context for configuration and sample conventions.
cURL
curl -X DELETE "${WALLKIT_API_BASE}/api/v1/user/profile" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/profile", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "DELETE",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/profile')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, method='DELETE')
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:
{
"status": "success"
}
Alternate result
The orchestrator failure returns this error; provider/local operations may have different outcomes. HTTP 404 excerpt:
{
"error": "delete_user_error",
"error_description": "Delete user profile has been failed"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_not_exists / resource_not_found | Missing context. | Restore the intended identity/resource before choosing deletion. |
| 404 | delete_user_error | Deletion orchestration exception. | Contact support to inspect remaining state; do not assume an atomic all-system rollback. |
Shared context errors apply before the action.
Next task
Detach a subscription membership
DELETE /api/v1/user/subscriptions/{id}
Store membership history, emit detach/optional marketing events and delete the matching user/Pricing relationship. It does not delete the Pricing, promise provider cancellation or initiate a refund. When auto_clear_content_views is enabled on the deleted plan’s resource, membership deletion can also remove this user’s AccessContent view records without a resource filter; this can affect view history across resources.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| id | path | numeric Pricing ID | required | Existing Pricing ID in this resource, not membership relationship ID; use catalog/profile membership data. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| result | boolean | Membership relationship delete result, not refund/provider-cancellation status. |
Example
Detach the member’s Pricing 2001 membership. See member context for configuration and sample conventions.
cURL
curl -X DELETE "${WALLKIT_API_BASE}/api/v1/user/subscriptions/2001" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
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 response = await fetch(url, {
method: "DELETE",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/subscriptions/2001')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, method='DELETE')
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:
{
"result": true
}
Alternate result
The user has no matching Pricing membership to detach. HTTP 404 excerpt:
{
"error": "user_subscription_not_exist",
"error_description": "Relations user subscription does not exist"
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_not_exists / subscriptions_not_exists / user_subscription_not_exist | Missing user/Pricing, other-resource Pricing or no matching membership. | Check Pricing ID and selected resource/member relationship. |
| 409 | store_user_subscription_history | History creation exception. | Contact support before repeating deletion. |
Shared context errors apply before the action.
Next task
Read the updated memberships and make fresh access decisions.
Change membership renewal flags
PUT /api/v1/user/subscriptions/{id}
Change a stored membership’s autorenew or next-subscription permission and enqueue its configured events. When both fields are supplied and autorenew succeeds, that branch returns before next-subscription is handled. No payment execution/provider cancellation/completion guarantee. Enable autorenew only when the Pricing permits it.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
JSON body.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| id | path | numeric Pricing ID | required | Existing Pricing ID in this resource, not membership relationship ID; use catalog/profile membership data. |
| autorenew | body | boolean-filtered input | optional; one useful field needed | Turn renewal flag on/off; successful branch takes precedence. |
| is_allowed_next_subscription | body | boolean-filtered input | optional | Turn next-subscription permission on/off when this branch is reached. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
| subscriptions[].id / plan_id | integer IDs | Pricing 2001 under Plan 1001 in this excerpt; the serializer does not include membership relationship ID. |
| subscriptions[].autorenew | boolean | Stored membership renewal choice; the successful example changes true to false. |
Example
Turn off autorenew for the existing Pricing 2001 membership. See member context for configuration and sample conventions.
cURL
curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/subscriptions/2001" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_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 response = await fetch(url, {
method: "PUT",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN,
"Content-Type": "application/json"
},
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')
body = {'autorenew': False}
request = Request(url, data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN'], "Content-Type": "application/json"}, method='PUT')
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": 42,
"email": "reader@example.com",
"subscriptions": [
{
"id": 2001,
"plan_id": 1001,
"subscription_start_date": "2030-01-01 00:00:00",
"autorenew": false
}
],
"teams": []
}
Alternate result
For the same existing Pricing 2001 membership initially having autorenew:true, an empty JSON object supplies neither handled flag and returns HTTP 400 with its unchanged user/membership projection, rather than a named validation error. HTTP 400 excerpt:
{
"id": 42,
"email": "reader@example.com",
"subscriptions": [
{
"id": 2001,
"plan_id": 1001,
"subscription_start_date": "2030-01-01 00:00:00",
"autorenew": true
}
],
"teams": []
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 400 | incorrect_data | Missing/non-JSON body. | Send the documented JSON object. |
| 404 | user_not_exists / subscriptions_not_exists / user_subscription_not_exist | Missing user/Pricing, other-resource Pricing or no matching membership. | Check Pricing ID and selected resource/member relationship. |
| 400 | user projection; optional exception field | No handled flags or helper failure/fallthrough. | Send one intended flag; check Pricing eligibility and current state. No universal helper409 error. |
Shared context errors apply before the action.
Next task
Replace the user photo
POST /api/v1/user/photo
Upload the first multipart file, replace this user’s existing photo media/storage objects, crop/store public photo variants and invalidate the photo cache. Replacement is global to the user and deletes old objects before completing new uploads; failure can leave partial state.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
Multipart upload; raw-image alternative described below.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
| photo | multipart file | image file | one file for this example | The first uploaded file is selected; the field name is not fixed by the handler. Detected PNG/JPEG/GIF/BMP required. No action-level size cap. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| top-level user | object fields | The profile user projection selected by this operation; no user/data wrapper. |
Example
Replace the member’s photo with one PNG file. See member context for configuration and sample conventions.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/user/photo" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}" \
-F "photo=@example-avatar.png;type=image/png"
JavaScript
// Node.js 18+; built-in fetch/FormData/Blob; synthetic local filename.
import { readFile } from "node:fs/promises";
async function main() {
const url = new URL("/api/v1/user/photo", process.env.WALLKIT_API_BASE);
const form = new FormData();
form.append("photo", new Blob([await readFile("example-avatar.png")], { type: "image/png" }), "example-avatar.png");
const response = await fetch(url, {
method: "POST",
headers: {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_TOKEN
},
body: form
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only; synthetic local filename.
import json
import os
import uuid
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
boundary = "wallkit-" + uuid.uuid4().hex
body = (
("--" + boundary + '\r\nContent-Disposition: form-data; name="photo"; filename="example-avatar.png"\r\nContent-Type: image/png\r\n\r\n').encode("utf-8")
+ Path("example-avatar.png").read_bytes()
+ ("\r\n--" + boundary + "--\r\n").encode("utf-8")
)
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/user/photo")
request = Request(url, data=body, headers={
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"firebase-token": os.environ["FIREBASE_ID_TOKEN"],
"Content-Type": "multipart/form-data; boundary=" + boundary
}, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
These use one synthetic PNG file and the same multipart field in every language; do not set a manual multipart Content-Type for cURL/FormData because their clients supply the boundary. JavaScript uses an ES module, such as an .mjs file. No authoring step reads or uploads the example file.
The alternate raw-image path consumes the request bytes when no multipart file is present. It rejects raw bodies shorter than 500 bytes before image validation. Raw JSON/base64 photo fields are not supported by this path; choose the documented multipart request.
HTTP 200 response excerpt:
{
"id": 42,
"email": "reader@example.com",
"photos": {
"image_100": "https://example.com/photos/image-100.jpg"
}
}
Alternate result
A selected non-image or unsupported image type is rejected before old-photo removal. HTTP 409 excerpt:
{
"error": "invalid_image",
"error_description": "Wrong image format. To upload a photo, use png, jpg, gif or bmp image types."
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 409 | invalid_image | Image detection, storage or cropping failed. | Use a supported image and ask integration owner about storage errors; old-photo deletion may already have happened. |
Shared context errors apply before the action.
Next task
Display the returned global photo map or read current profile.
Understand the unavailable history-write route
POST /api/v1/user/history
This registered operation immediately throws “Elasticsearch not available”. It cannot perform its intended history-write job in this source. No accepted body, successful response or resulting history record is established; the legacy body after the throw is unreachable.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
No action-defined successful HTTP response.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| success response | not available | No reachable success projection. |
| exception text | string | Elasticsearch not available; no action-defined HTTP status/API error wrapper. |
Example
This request illustrates the unavailable route only; there is no success response to pair with it. See sample runtimes.
cURL
curl -X POST "${WALLKIT_API_BASE}/api/v1/user/history" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/history", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "POST",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/history')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, method='POST')
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
Alternate result
All action invocations hit the same unconditional exception; no separate supported outcome is defined.
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| unspecified | unconditional exception: Elasticsearch not available | History storage disabled at entry. | Handle unavailable operation; contact the integration owner. Do not rely on the unreachable body or retry for success. |
Shared context errors apply before the action.
Next task
List the member’s tags
GET /api/v1/user/tags
Read the member’s tag slugs for the selected resource and global tags with resource_id 0. No pagination, deduplication or ordering guarantee. This filtered read differs from profile-update tag replacement, which truncates all user tag relationships.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| items | array of strings | Tag slug values; empty when no selected tags. No full tag objects. |
Example
Read this resource’s tags, including applicable global tags. See member context for configuration and sample conventions.
cURL
curl -X GET "${WALLKIT_API_BASE}/api/v1/user/tags" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/tags", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "GET",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/tags')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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:
{
"items": [
"techno"
]
}
Alternate result
No matching tag relationship returns an empty collection. HTTP 200 excerpt:
{
"items": []
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_not_exist | No resolved user; the request stops. | Restore valid member context before reading tags. |
Shared context errors apply before the action.
Next task
Use profile update only when you intend its all-resource tag replacement.
Read subscription-change history
GET /api/v1/user/plans/history
Read historical membership rows for this member/resource, ordered by relationship_id descending. Missing joined plan/Pricing details fall back to stored payload fields. Exceptions are caught and can return an empty or partial collection, so empty does not prove no history exists.
Before you call
Existing member session for the selected resource. See member context for shared checks and the ordinary/Firebase header difference.
Request
No action body is consumed by this documented path.
| Name | Location | Type | Requirement / default | Meaning and constraints |
|---|---|---|---|---|
| resource | header | string | required | Public resource key for intended member context. |
| token | header | string | required member context | Existing Wallkit session token. |
| firebase-token | header | string | Firebase-enabled member only | Matching Firebase ID token; omit for an ordinary resource. |
Result
HTTP 200 on the primary branch.
| Field / projection | Type / presence | Meaning |
|---|---|---|
| items | array | Profile history rows; no paginator. This differs from nested subscriptions_history on GET user. |
Example
Read historical membership rows for this member and resource. See member context for configuration and sample conventions.
cURL
curl -X GET "${WALLKIT_API_BASE}/api/v1/user/plans/history" \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "firebase-token: ${FIREBASE_ID_TOKEN}"
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/user/plans/history", process.env.WALLKIT_API_BASE);
const response = await fetch(url, {
method: "GET",
headers: {
"resource": process.env.RESOURCE_KEY,
"token": process.env.USER_TOKEN,
"firebase-token": process.env.FIREBASE_ID_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 urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], '/api/v1/user/plans/history')
request = Request(url, headers={'resource': os.environ['RESOURCE_KEY'], 'token': os.environ['USER_TOKEN'], 'firebase-token': os.environ['FIREBASE_ID_TOKEN']}, 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:
{
"items": [
{
"relationship_id": 9001,
"plan_id": 1001,
"plan_title": "Reader plan",
"subscription_id": 2001,
"subscription_title": "Monthly reader",
"subscription_start_date": "2030-01-01 00:00:00",
"subscription_end_date": "2030-02-01 00:00:00",
"is_trial_subscription": false,
"is_attached_by_admin": false,
"admin_id": null
}
]
}
Alternate result
No selected rows or a caught query/projection exception can produce an empty collection. HTTP 200 excerpt:
{
"items": []
}
Recovery
| HTTP status | API code / shape | Cause | Next action |
|---|---|---|---|
| 404 | user_not_exist / resource_not_exist | Missing user or resource; the request stops. | Restore the matching member and resource context before reading history. |
| 200 | empty/partial items | No records or caught read exception. | Show available history; contact support if expected records are missing. |
Shared context errors apply before the action.
Next task
Use current memberships and access decisions for present permission.