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

TaskMethod / path
Remove Wallkit refresh recordsPOST /api/v1/cleanup-refresh
Read the current member profileGET /api/v1/user
Update profile fields and tagsPUT /api/v1/user
Set an initial resource passwordPOST /api/v1/user/password
Change an existing resource passwordPUT /api/v1/user/password
Check whether a password is storedGET /api/v1/user/has-password
Request an email changePUT /api/v1/user/email
Link a provider identityPUT /api/v1/user/social
Unlink a provider identityPOST /api/v1/user/social/delete
Suspend the member in this resourcePOST /api/v1/user/suspend
Remove the member profile from this resourceDELETE /api/v1/user/profile
Detach a subscription membershipDELETE /api/v1/user/subscriptions/{id}
Change membership renewal flagsPUT /api/v1/user/subscriptions/{id}
Replace the user photoPOST /api/v1/user/photo
Understand the unavailable history-write routePOST /api/v1/user/history
List the member’s tagsGET /api/v1/user/tags
Read subscription-change historyGET /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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
msgobjectMessage envelope, not a top-level result.
msg.resultbooleantrue after cleanup is invoked; no removed-record count.
msg.msgstringFixed 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 statusAPI code / shapeCauseNext action
404user_not_existMissing 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringoptionalPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe 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 statusAPI code / shapeCauseNext action
context-dependentShared identity/context errorsNo 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
nick_namebodystring / nulloptional; presence-sensitiveNickname normalization/uniqueness validation, filtered length3–64; null does not guarantee clearing.
first_namebodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
last_namebodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
countrybodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
citybodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
statebodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
addressbodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
ipbodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
companybodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
jobbodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
zipbodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
phonebodystringoptional; issetTrimmed profile text; omission/null leaves it unchanged; no action-level length limit here.
extrabodyobjectoptionalSelected resource relationship extra; helper merges its supplied keys, not this separate extra-data group API.
language_idbodyinteger-filtered inputoptional; nonemptyExisting language ID. No clearing behavior for empty/null.
language_slugbodystringoptional; nonemptyExisting language slug; processed after language_id and overrides it when both present.
tagsbodyarrayoptionalReplacement tag list; [] removes all user tag relationships across resources. Each item nonempty length3–25, not object/array. Non-array input is ignored.
emailbodyunsupported non-null fielddo not sendUse the separate email-change operation.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe profile user projection selected by this operation; no user/data wrapper.
tagsarray of tag slug strings; conditionalIncluded 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 statusAPI code / shapeCauseNext action
400incorrect_dataMissing/non-JSON body.Send the documented JSON object.
409incorrect_requestEmail sent in this profile body.Use PUT /api/v1/user/email.
409nickname validation code / invalid_language_id / invalid_language_slug / invalid_tags_format / invalid_tagInvalid nickname, language or tag.Correct the named input; earlier resource writes may already exist.
406 / 422update_user_fail / update_users_failTag/helper/user-save failure.Inspect current profile state before repeating the write.

Shared context errors apply before the action.

Next task

Read the profile to display the resulting member context.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
passwordbodystringrequiredLength6–40, trimmed for storage.
password_confirmbodystringrequired matching confirmationMust match password.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
resultbooleantrue 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 statusAPI code / shapeCauseNext action
400incorrect_dataMissing/non-JSON body.Send the documented JSON object.
404user_resource_not_existNo resource relationship.Establish this resource membership first.
422user_resource_password_exist / invalid_passwordExisting password or invalid length/confirmation.Use password-update for an existing password; otherwise correct length/confirmation.
406set_password_failProvider/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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
old_passwordbodystringrequired only when resource password nonemptyVerified against the applicable stored password.
passwordbodystringoptional alternativeWhen nonempty, length6–40 and password_confirm match; copied to new_password.
password_confirmbodystringrequired with nonempty passwordConfirmation for password branch.
new_passwordbodystringrequired when password branch unusedLength6–40; this alternative has no confirmation field check.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
resultbooleantrue 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 statusAPI code / shapeCauseNext action
400incorrect_dataMissing/non-JSON body.Send the documented JSON object.
422invalid_<field>Missing/invalid password input.Correct required old/new length/confirmation inputs.
406change_password_failIncorrect old password or missing looked-up user.Correct current password or contact support.
406save_password_failProvider/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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
statusbooleantrue 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 statusAPI code / shapeCauseNext action
404user_resource_not_exist / user_password_not_setMissing 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
emailbodystringrequiredValid email, max60; trimmed/lowercased; cannot equal current email or another global user’s email.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
resultbooleantrue 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 statusAPI code / shapeCauseNext action
422invalid_emailMissing/invalid format or length.Correct email format/length.
422invalid_email; E-mail address already in use.Address belongs to an existing global user.Choose the intended unused address.
422invalid_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.
409invalid_new_emailSame email or globally used email.Choose the intended unused address.
409error_create_hashConfirmation hash save failed.Contact support; delivery and email change are not established.

Shared context errors apply before the action.

Next task

Confirm using the received configured code flow.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
methodbodystringrequiredgoogle or facebook for a useful implemented branch; no inclusion validator here.
databodyobjectoptionalWhen supplied, id/access_token are read from this nested body instead.
idbody or datastringneeded for actual linkingProvider user ID, unique across global Google/Facebook-linked users.
access_tokenbody or datastringneeded for provider verificationProvider access token matching id; not a Wallkit/Firebase token.

Result

HTTP 201 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe 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 statusAPI code / shapeCauseNext action
400incorrect_data / account_errorMissing method or provider ID already used.Supply the implemented method or choose the correct existing linked account.
406invalid_social_idVerified provider ID differs.Supply the access token and ID from the same provider identity.
409error_socialProvider/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.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
methodbodystringrequiredgoogle or facebook selects the global field to clear. No inclusion validator.

Result

HTTP 201 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe 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 statusAPI code / shapeCauseNext action
400incorrect_dataMissing method.Choose the intended implemented provider.
404user_not_existsUser not found in resource lookup.Restore the intended member/resource context.
409error_delete_socialSave orchestration failed.Read current identity state before repeating.

Shared context errors apply before the action.

Next task

Ensure the member has another authorized sign-in flow.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe 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 statusAPI code / shapeCauseNext action
404resource_not_existsMissing/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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
statusstringsuccess 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 statusAPI code / shapeCauseNext action
404user_not_exists / resource_not_foundMissing context.Restore the intended identity/resource before choosing deletion.
404delete_user_errorDeletion 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

Return the app to a signed-out state and handle future membership through an authorized identity flow.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
idpathnumeric Pricing IDrequiredExisting Pricing ID in this resource, not membership relationship ID; use catalog/profile membership data.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
resultbooleanMembership 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 statusAPI code / shapeCauseNext action
404user_not_exists / subscriptions_not_exists / user_subscription_not_existMissing user/Pricing, other-resource Pricing or no matching membership.Check Pricing ID and selected resource/member relationship.
409store_user_subscription_historyHistory 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
idpathnumeric Pricing IDrequiredExisting Pricing ID in this resource, not membership relationship ID; use catalog/profile membership data.
autorenewbodyboolean-filtered inputoptional; one useful field neededTurn renewal flag on/off; successful branch takes precedence.
is_allowed_next_subscriptionbodyboolean-filtered inputoptionalTurn next-subscription permission on/off when this branch is reached.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
top-level userobject fieldsThe profile user projection selected by this operation; no user/data wrapper.
subscriptions[].id / plan_idinteger IDsPricing 2001 under Plan 1001 in this excerpt; the serializer does not include membership relationship ID.
subscriptions[].autorenewbooleanStored 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 statusAPI code / shapeCauseNext action
400incorrect_dataMissing/non-JSON body.Send the documented JSON object.
404user_not_exists / subscriptions_not_exists / user_subscription_not_existMissing user/Pricing, other-resource Pricing or no matching membership.Check Pricing ID and selected resource/member relationship.
400user projection; optional exception fieldNo 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

Read memberships to display the stored flag outcome.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.
photomultipart fileimage fileone file for this exampleThe 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 / projectionType / presenceMeaning
top-level userobject fieldsThe 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 statusAPI code / shapeCauseNext action
409invalid_imageImage 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

No action-defined successful HTTP response.

Field / projectionType / presenceMeaning
success responsenot availableNo reachable success projection.
exception textstringElasticsearch 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 statusAPI code / shapeCauseNext action
unspecifiedunconditional exception: Elasticsearch not availableHistory 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

Read supported profile/history operations instead.

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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
itemsarray of stringsTag 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 statusAPI code / shapeCauseNext action
404user_not_existNo 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.

NameLocationTypeRequirement / defaultMeaning and constraints
resourceheaderstringrequiredPublic resource key for intended member context.
tokenheaderstringrequired member contextExisting Wallkit session token.
firebase-tokenheaderstringFirebase-enabled member onlyMatching Firebase ID token; omit for an ordinary resource.

Result

HTTP 200 on the primary branch.

Field / projectionType / presenceMeaning
itemsarrayProfile 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 statusAPI code / shapeCauseNext action
404user_not_exist / resource_not_existMissing user or resource; the request stops.Restore the matching member and resource context before reading history.
200empty/partial itemsNo 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.

Full diagram

Use the arrow keys to scroll. Escape closes this view.

Search documentation

Enter at least 2 characters.

    ↑ ↓ move through results · Enter opens · Escape closes