Manage teams

Read teams you administer and create individual invitations for an eligible team. Being able to see a team does not prove you can manage it or activate a membership.

TaskOperation
Read owner/admin teamsTeam list
Create a teamCreate a team
Update team display fieldsUpdate team display fields
Delete a team recordDelete a team record
Read team detailRead team detail
List team invitationsList team invitations
Read one team invitationRead one team invitation
Update invitation fieldsUpdate invitation fields
Delete an invitationDelete an invitation
Request invitation resendRequest invitation resend
Create an individual invitationCreate invite

Member relationships

TaskOperation
List non-owner membersList non-owner members
Read one memberRead one member
Update role/status or requested PricingUpdate role/status or requested Pricing
Mark a member removedMark a member removed
Mark a member suspendedMark a member suspended

Admin-prefixed registration

Task / access distinctionOperation
Resend using admin-prefix session resolution; same user ACL/action guardAdmin-prefixed resend

Example clients and synthetic values follow response conventions.

Member context

Existing member context uses custom token and resource headers; see credential transport. Team actions require an active user; initialization has no separate required-resource guard, but context resolution and action eligibility can depend on resource. Configured Firebase handling follows the selected integration. A visible team does not prove management permission.

Selected-team management eligibility

For operations that link here, the action accepts either selected-resource can-own-teams eligibility or an active owner/admin relationship for the selected team. Eligibility checks a qualifying Plan relationship in the selected resource, without active/expiration filtering. That branch can pass without ownership of the selected team. Team lookup has no resource filter; do not infer a stricter owner/resource guarantee.

The team list uses its own role/current-user selection. Team creation has a separate eligibility check described beside that operation. Neither inherits this selected-team management rule.

Resend context and effects

Use the shared member context. Resend permits selected-resource can-own-teams eligibility, active team owner/admin, or global user role admin/root. A route prefix does not substitute for those checks.

Select the intended team/invite pair. Lookup is not resource-filtered; invite.team_id must match the selected team. Resend does not revalidate invite email/domain/window/cap/used status/Pricing capacity.

Queue failure can be swallowed; result:true acknowledges this action, without confirming delivered email. Repeated calls can attempt more notifications; there is no deduplication guarantee.

Read your owner/admin teams

GET /api/v1/user/teams

List teams linked to the current user through owner/admin roles. This list has no active relationship/team or resource filter; do not use it as a write-permission or active-access decision.

Before you call

Use the shared member context.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
token, resourceheadersexisting member contextIdentify current member as described above.
filterqueryoptional bracket-style model-field mapTeams metadata filter; use filter[Teams.id] for a known ID, not an arbitrary authorization selector. No resource restriction added by this filter.
byquerystring, default Teams.idDirect ordering expression; no strict allowlist established.
sortquerystring, default DESCDirect sort expression; no strict enum validation established.
limitqueryinteger-filter then cast; default 10Outer team page size; no explicit upper bound.
pagequeryraw value cast to integerSend page=1 explicitly; no explicit default/strict validation.

Result

HTTP 200 items array, empty [] possible, with common paginator. Each item is the team-list projection; invites are unpaginated/all-associated. Default order Teams.id DESC; team grouping avoids duplicate team IDs.

Example: Identify a team before checking invitation eligibility

Read the first page. This excerpt shows team 1001 and empty embedded invite/Pricing collections; additional full-team fields are omitted.

curl "${WALLKIT_API_BASE}/api/v1/user/teams?page=1&limit=10" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams?page=1&limit=10`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams?page=1&limit=10",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "items": [
    {
      "id": 1001,
      "name": "Example Media",
      "users_count": 1,
      "invites_count": 0,
      "invites": [],
      "subscription": []
    }
  ],
  "paginator": {
    "current_page": 1,
    "page": 1,
    "total_pages": 1,
    "total_items": 1,
    "limit": 10
  }
}

Consequential alternate

No owner/admin role relationship matches, HTTP 200 excerpt:

{
  "items": []
}

Empty is not an invitation or ownership grant. A suspended relationship can still make a team visible here because status is not filtered.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failUser context absent/inactive/locked/suspended.Follow credential recovery.
200items:[]No selected owner/admin team rows.Handle an empty page; check intended account/filter with integration owner.
UnspecifiedNo dedicated list error envelopeQuery/filter/order failure.Correct model-field filters/order values; no stable list-specific error code promised.

Next task

Use the returned team ID only after reading invite creation prerequisites. See the Group-memberships (teams) management.

Create an individual team invitation

POST /api/v1/user/teams/{team_id}/invites

Save an invitation and attempt its individual_in_team_invite queue event. HTTP 201 is not delivered email or activated membership.

Before you call

Use the shared member context.

This action uses selected-team management eligibility.

Email must not already have any invite for this team, must match configured domain patterns, and the selected Pricing must pass team eligibility/seat checks. A positive linked users_limit counts active team Pricing relationships; paid Pricing additionally requires a payment customer on an active owner’s globally inspected account. This presence check does not charge or prove a usable/default source. Queue failure can be swallowed; save result is unchecked and later failure may leave partial state.

Request

JSON object required.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsTeam ID from intended managed team, not relationship/invite ID.
token, resourceheadersexisting member/resource contextContext used by eligibility and stored invite.resource_id.
emailJSONrequired valid email, max 60 before sanitizationStored email/trim/lower; duplicate invite/domain checks.
subscription_idJSONrequired existing Pricing IDPresence/existence and active linked-Plan/Pricing eligibility; permission query has no current-user condition. Stored integer-filter value.
user_nameJSONoptional stringStored string/trim; no explicit presence/length validation; supply explicitly if used.
titleJSONoptional nonempty string, max 128Default Invite for <team name> team on omission/null.
descriptionJSONoptional stringSame team-derived default on omission/null; string/trim storage.
resource_idJSONoptional existing resource IDValidated when set but not used for storage; selected header resource is stored instead.
codeJSONoptional nonempty string, length 10–40 when setSupplied code is validated but not assigned. Omit to use generated 40-character code; otherwise save hook generates UUID when empty. No accepted-custom-code guarantee.

Result

HTTP 201 top-level invite record, not data/invite wrapper. Generated code and invite ID are distinct. activations_limit is set to 1; no explicit start/end window assigned. Conditional subscription/team/ticket fields follow that exact projection.

Example: Create one invite for team 1001

Choose an already eligible free Pricing 2001, so the example does not imply owner payment or paid activation. Omit code/resource_id; header resource controls storage. The synthetic code below represents the returned generated code, not a supplied custom code.

curl -X POST "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"email": "reader@example.com", "user_name": "Alex Reader", "subscription_id": 2001}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"email": "reader@example.com", "user_name": "Alex Reader", "subscription_id": 2001})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'email': 'reader@example.com', 'user_name': 'Alex Reader', 'subscription_id': 2001}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 201 excerpt:

{
  "id": 3001,
  "email": "reader@example.com",
  "user_name": "Alex Reader",
  "title": "Invite for Example Media team",
  "code": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001",
  "activations_limit": 1
}

Store the returned code for the intended invitee flow; ID 3001 is for invitation management.

Consequential alternate

An existing invite for this email/team returns HTTP 409 excerpt (other metadata omitted):

{
  "error": "invalid_email",
  "error_description": "This user already has an invitation to your team. Try sending an invitation again."
}

Creation does not overwrite that invite. Inspect the existing record and intended resend policy; the literal message is not proof that retry/resend is harmless.

Recovery

HTTP statusAPI code / shapeCauseNext action
401incorrect_data; You do not have permission to manage teamsNeither eligibility nor active team-admin/owner check passes.Ask integration owner to check intended resource/Plan/team role.
406incorrect_data; Body must be json formatNo truthy decoded JSON body.Submit JSON with required email/Pricing.
404team_not_foundNo team at selected ID.Check intended returned team ID.
409invalid_email / invalid_subscription_id / invalid_title / invalid_code / invalid_resource_idValidator failure; includes duplicate/domain/linked seat/payment-customer checks.Correct indicated input or inspect eligibility/capacity with integration owner; never assume invitation reserves a seat.
409exception; Please contact to administratorCaught save/event/refresh/serialization error.Reconcile saved invite before another creation attempt.

Next task

The invitee can validate the supplied code, then use their own active member context for activation. See Group-memberships (teams) management.

Create a team record

POST /api/v1/user/teams

Create a team with an active owner relationship; no membership purchase or invitation is automatic.

Before you call

Use the shared member context.

Creation requires selected-resource can-own-teams eligibility. The alternative team-admin helper is called without a team ID and checks team 0, so it does not establish a general “admin of any team” permission. The action starts a DB transaction, saves team unchecked, attempts team_owner_create_team queue event, then saves owner relationship unchecked and commits. No atomic queue/local persistence or duplicate-creation guarantee.

Team active=true, stored owner=current user, member_limit=0. Resource auto_allowed_domains_for_team can derive allowed domain from current user’s valid email. Team record has no resource_id assigned, and no partner relationship/membership is created by this action.

Request

JSON object required.

NameLocationType / requirementMeaning / constraint
token, resourceheadersexisting member/resource contextEligibility and event context.
nameJSONrequired string, max 255 before sanitizationString/trim storage.
descriptionJSONoptional string / nullStored only when nonnull; string/trim.
typeJSONoptional nonempty accepted stringpersonal, company, company_subdivision, organization, educational_institution, multi_user; string/trim/lower storage. Omission has no explicit handler default.
member_limitJSONignoredHandler always sets 0; cannot configure seats here.

Result

HTTP 201 top-level team detail with users/invites, not a list item/data wrapper. No team Pricing field is added by this projection.

Example: Create an explicitly typed company team

Create Example Media with type company rather than guessing an omitted type default. Response excerpt omits owner/member detail.

curl -X POST "${WALLKIT_API_BASE}/api/v1/user/teams" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"name": "Example Media", "type": "company", "description": "Example publishing team"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"name": "Example Media", "type": "company", "description": "Example publishing team"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'name': 'Example Media', 'type': 'company', 'description': 'Example publishing team'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 201 excerpt:

{
  "id": 1001,
  "name": "Example Media",
  "type": "company",
  "active": true,
  "member_limit": 0,
  "invites": [],
  "users_count": 1
}

Consequential alternate

If owner relationship handling throws, HTTP 406 excerpt (other metadata omitted):

{
  "error": "incorrect_owner",
  "error_description": "Create owner team id fail"
}

Rollback is attempted, but the queue event may already have been attempted. Unchecked false saves do not necessarily enter this exception branch.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
406incorrect_data; Body must be json formatNo truthy JSON body.Supply object with required name.
409invalid_name / invalid_typeLength or type validation failure.Use a valid length/type value.
406incorrect_ownerCaught owner relationship exception.Reconcile team/owner/event state before another create attempt.
UnspecifiedNo dedicated outer save/event error envelopeFailure before owner try block or after commit.Ask integration owner to inspect persisted state; avoid blind creation retry.

Next task

Use team detail and invite creation only with intended eligibility. Creation alone grants no purchased content.

Update team display fields

PUT /api/v1/user/teams/{team_id}

Change supplied name/description/type; this action does not update seat limits, team Pricing or active status.

Before you call

Use the shared member context and selected-team management eligibility.

Completion is not established by the reviewed source. The action attempts a save and refresh before calling a change-comparison helper that is missing from the controller’s included traits. A change can therefore be saved before failure. There is no dedicated error handling for this failure. The intended result below applies only if that helper is available in the deployment.

The permitted fields do not replace memberships or history. Pricing, membership end-date and lock-prevention changes are not selectable here. The save result is not checked, and no durable-save or delivered-email guarantee follows.

Request

JSON object required; omitted/null editable fields stay unchanged.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID; not user/invite/relationship ID.
token, resourceheadersexisting member/resource contextSelect member and eligibility context.
nameJSONoptional string, max 255String/trim; no presence validation when set.
descriptionJSONoptional stringString/trim; no explicit length validation.
typeJSONoptional nonempty accepted stringSame six values as creation.
member_limit, subscription_id, active, never_lockJSONignoredNo assignment branch in this action.

Result

Intended HTTP 200 top-level team detail with users/invites, not a list item/data wrapper. No team Pricing field is added by this projection.

Example: Rename team without changing seats

This request expresses an intended name-only update for team 1001. A reachable completed success example is N/A in the available source because the post-save comparison helper is unresolved; the conditional projection below teaches the intended response without certifying it.

curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/teams/1001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"name": "Example Newsroom"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001`, {
  method: "PUT", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"name": "Example Newsroom"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'name': 'Example Newsroom'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Intended synthetic HTTP 200 projection only, conditional on the unresolved helper being available:

{
  "id": 1001,
  "name": "Example Newsroom",
  "type": "company",
  "member_limit": 0
}

Consequential alternate

A missing selected team returns HTTP 404 excerpt (other metadata omitted):

{
  "error": "team_not_found",
  "error_description": "Team not found"
}

The unresolved post-save helper can instead fail after the name has been saved. A false save can also leave original fields after refresh; do not treat status alone as confirmed requested change.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
404team_not_foundTeam lookup fails.Check intended team ID; list visibility does not grant writes.
406incorrect_data; Body must be json formatNo truthy JSON body.Send object with intended supported fields.
409invalid_name / invalid_typeSupplied field validation fails.Correct length/type; ignored fields cannot configure capacity.
UnspecifiedNo dedicated save/event exception envelopeSave/refresh or unresolved post-save change helper fails.Read intended detail/reconcile before repeating changes.

Next task

Inspect returned/read-back display fields with team detail; do not infer membership/billing changes.

Delete the selected team record

DELETE /api/v1/user/teams/{team_id}

Attempt model deletion and return its Boolean result. This is distinct from removing or suspending a team member.

Before you call

Use the shared member context.

This action uses selected-team management eligibility.

The action calls Team.delete with no explicit relationship/invite/membership/history cleanup or refund/account-deletion sequence. Team model has no beforeDelete/afterDelete hook defining a cascade here. Database constraints/cascades may affect the outcome; no universal cascade or preserved-access promise follows. No action event is emitted. Read result even on HTTP 200.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID; not user/invite/relationship ID.
token, resourceheadersexisting member/resource contextSelect member and eligibility context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
idintegerSelected team ID.
resultbooleanReturn value of model delete; false is not successful deletion.

Example: Interpret the delete result for team 1001

Only for the intended deletion task, construct this request. No user/provider account deletion or refund is implied.

curl -X DELETE "${WALLKIT_API_BASE}/api/v1/user/teams/1001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001`, {
  method: "DELETE", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 1001,
  "result": true
}

Consequential alternate

Model deletion can return false with HTTP 200:

{
  "id": 1001,
  "result": false
}

This indicates unsuccessful deletion, without a returned model-error detail. Do not infer account/access removal or retry blindly.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
404team_not_foundTeam lookup fails.Check intended team ID; list visibility does not grant writes.
200result:falseModel delete returned false.Ask integration owner to inspect constraints/state before deciding another attempt.
UnspecifiedNo dedicated deletion catchDelete throws or other handling fails.Reconcile selected record/dependent state; no cascade/refund promise.

Next task

Reconcile intended team and member/account state with integration owner. Do not equate team deletion with individual user deletion or an access decision.

Read one managed team detail

GET /api/v1/user/teams/{team_id}

Read team fields with all associated invitations and users. The result differs from the list’s embedded Pricing projection.

Before you call

Use the shared member context.

This action uses selected-team management eligibility.

After eligibility, a joined query combines an owner/admin-role condition OR current-user condition and a team-ID condition, with no explicit resource/status filter. Predicate grouping is not an independently enforced ownership guarantee; inspect returned id against your intended team. It requires a joined relationship row, unlike a simple unjoined team lookup.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID; not user/invite/relationship ID.
token, resourceheadersexisting member/resource contextSelect member and eligibility context.
byqueryoptional string, default Teams.idDirect order expression, no strict allowlist.
sortqueryoptional string, default DESCDirect sort expression; ties unspecified.

Result

HTTP 200 top-level team detail with users/invites, not a list item/data wrapper. No team Pricing field is added by this projection.

Example: Read team 1001 without treating detail as a Pricing list

Inspect id/name/counts and role/status for each returned user in the full projection. This excerpt omits sensitive nested contact/invite fields.

curl "${WALLKIT_API_BASE}/api/v1/user/teams/1001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 1001,
  "name": "Example Media",
  "users_count": 1,
  "invites_count": 0,
  "invites": [],
  "users": [
    {
      "id": 4001,
      "role": "owner",
      "status": "active"
    }
  ]
}

Consequential alternate

No joined team row selected returns HTTP 404 excerpt (other metadata omitted):

{
  "error": "team_not_found",
  "error_description": "Team not found"
}

A bare team record can exist without satisfying this joined selection.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
404team_not_foundTeam lookup fails.Check intended team ID; list visibility does not grant writes.
UnspecifiedNo dedicated detail query catchQuery/order/serialization failure.Check ID/order values and intended relationship state with integration owner.

Next task

Use invite list for its separate pagination/projection. Team detail is no substitute for content access.

List a team’s invitation records

GET /api/v1/user/teams/{team_id}/invites

Page invitation records for the selected team, including usage. No available-only/time/email/resource filtering is automatic.

Before you call

Use the shared member context.

This action uses selected-team management eligibility.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID; not user/invite/relationship ID.
token, resourceheadersexisting member/resource contextSelect member and eligibility context.
filterqueryoptional bracket model-field mapInvites metadata filter, alongside enforced team_id.
byquerystring, default Invites.idDirect ordering expression.
sortquerystring, default DESCDirect sort expression, no strict enum validation.
limitqueryinteger-filter then cast, default 10Page size, no explicit upper bound.
pagequeryraw value cast integerUse page=1; no explicit default/strict validation.

Result

HTTP 200 items array and common paginator. Each item is invite-list projection, not the embedded team-list usage/mail projection.

Example: Inspect invite 3001 and activation count

Read first invitation page for team 1001. This excerpt omits rich record fields.

curl "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites?page=1&limit=10" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites?page=1&limit=10`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites?page=1&limit=10",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "items": [
    {
      "id": 3001,
      "email": "reader@example.com",
      "activations": 0,
      "used": null
    }
  ],
  "paginator": {
    "current_page": 1,
    "page": 1,
    "total_pages": 1,
    "total_items": 1,
    "limit": 10
  }
}

Consequential alternate

No matching invitation rows returns HTTP 200 excerpt:

{
  "items": []
}

This does not describe delivered mail or active members.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
404team_not_foundTeam lookup fails.Check intended team ID; list visibility does not grant writes.
200items:[]No invite matches selected team/filter.Handle empty list; check intended team and filter.
UnspecifiedNo dedicated list error catchFilter/query/serialization fails.Correct model-field filters/order; ask integration owner about stored data.

Next task

Read one invite detail using its invite ID; pass its code, not ID, to invitee validation.

Read a selected team invitation

GET /api/v1/user/teams/{team_id}/invites/{id}

Read one invite only after its team ID matches the selected team.

Before you call

Use the shared member context.

This action uses selected-team management eligibility.

Team and invite are fetched by IDs without resource filtering, then invite.team_id must equal selected team.id. This association check differs from the eligibility branch; it does not prove current user owns the team.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID; not user/invite/relationship ID.
token, resourceheadersexisting member/resource contextSelect member and eligibility context.
idpathrequired digitsInvitation record ID from list/create result; not invite code.

Result

HTTP 200 top-level invite record. No activations/used/send_mails/available fields are added by this detail serializer.

Example: Read invite 3001 under team 1001

Use the invitation ID from creation/listing; response excerpt shows its redeemable code separately.

curl "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites/3001",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 3001,
  "email": "reader@example.com",
  "code": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001",
  "activations_limit": 1
}

Consequential alternate

Invite belongs to another team, HTTP 401 excerpt (other metadata omitted):

{
  "error": "invite_access",
  "error_description": "This invitation is not available to your team!"
}

Changing the user token does not change the record’s team association.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMissing/inactive/locked member context.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check selected resource/Plan/team role with integration owner.
404team_not_foundTeam lookup fails.Check intended team ID; list visibility does not grant writes.
404invite_not_foundInvite ID does not resolve.Check record ID from intended list/create result.
401invite_accessInvite.team_id differs from selected team.Use intended matching team/invite pair; never probe another team’s code.
UnspecifiedNo dedicated serializer catchRelated record projection fails.Ask integration owner to inspect invite/team/Pricing data.

Next task

When intended, give the supplied code to the invitee’s Group-memberships (teams) management; reading detail does not send or activate it.

Update an invitation’s supplied fields

PUT /api/v1/user/teams/{team_id}/invites/{id}

Change recipient/display/Pricing fields on a saved invite. No resend event is active in this update.

Before you call

Use the shared member context and selected-team management eligibility. Team and invite are looked up by IDs without resource filtering, then invite.team_id must match team.id. This checks the selected pair, not stronger owner/resource scope.

Email is validated but this update does not repeat create’s duplicate-email/domain validator. Changed Pricing repeats existence/team eligibility/seat/payment-customer validation; unchanged supplied Pricing skips those validators but is still assigned. Save is unchecked then refreshed; failure can leave state changed. An empty existing code can be generated, but a supplied code is ignored. No activation reset/expiry/cap update is supported.

Request

JSON object required; omitted/null fields remain unchanged.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended managed team ID.
idpathrequired digitsInvite record ID from create/list; not redeemable code.
token, resourceheadersexisting contextMember identity and selected resource/eligibility/event context.
emailJSONoptional nonempty valid email, max 60Email sanitized on storage without explicit trim/lower normalization used by creation.
titleJSONoptional nonempty string, max 128String/trim stored.
descriptionJSONoptional stringString/trim stored; no explicit length validation.
subscription_idJSONoptional existing Pricing IDIf changed, validates Pricing/team linked eligibility; stored int-filter value. Not membership ID.
code, activations_limit, start_date, end_date, resource_idJSONignoredNo assignment branch for these inputs.

Result

HTTP 200 top-level invite record. No usage/mail/available fields are added.

Example: Change invite 3001’s display title without resending

Update title only; the returned code remains the existing code unless it was empty before save.

curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"title": "Join Example Newsroom"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001`, {
  method: "PUT", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"title": "Join Example Newsroom"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'title': 'Join Example Newsroom'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites/3001",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 3001,
  "email": "reader@example.com",
  "title": "Join Example Newsroom",
  "code": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001"
}

Consequential alternate

A mismatched invite/team pair returns HTTP 401 excerpt (other metadata omitted):

{
  "error": "invite_access",
  "error_description": "This invitation is not available to your team!"
}

No intended update occurs through this rejected pair.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMember context fails.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check intended team/Plan/resource/role with integration owner.
404team_not_found / invite_not_foundSelected record absent.Check intended team/invite record IDs.
401invite_access; This invitation is not available to your team!Invite belongs to another team.Use the intended matching team/invite pair.
406incorrect_data; Body must be json formatNo truthy JSON body.Supply object with intended supported fields.
409invalid_email / invalid_title / invalid_subscription_idSupplied-field validation fails.Correct indicated field or inspect Pricing eligibility/capacity.
409exception; Please contact to administratorCaught save/refresh/serialization error.Reconcile saved invite before another change.

Next task

Read invite detail for current fields. If sending again is intended, separately read resend.

Delete an invitation record

DELETE /api/v1/user/teams/{team_id}/invites/{id}

Attempt deletion of the saved invitation; it does not remove an already activated user or membership.

Before you call

Use the shared member context and selected-team management eligibility. Team and invite are looked up by IDs without resource filtering, then invite.team_id must match team.id. This checks the selected pair, not stronger owner/resource scope.

The action does not reject an already-used invite and performs no explicit activation-history/membership cleanup or email recall. No model deletion hook/cascade is declared for Invites; database constraints/cascades can affect result. No delete event is emitted; inspect Boolean result.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended managed team ID.
idpathrequired digitsInvite record ID from create/list; not redeemable code.
token, resourceheadersexisting contextMember identity and selected resource/eligibility/event context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultbooleanModel delete return, not membership removal.

Example: Delete invite 3001 without treating it as member removal

To remove invitation 3001 from team 1001, use the manager context that passes this action’s eligibility check. The operation attempts invitation-record deletion; it does not remove a team member.

curl -X DELETE "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001`, {
  method: "DELETE", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites/3001",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "result": true
}

Consequential alternate

Delete can return false with HTTP 200:

{
  "result": false
}

No model-error detail is serialized. An already activated member’s relationship is not explicitly removed by this action.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMember context fails.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check intended team/Plan/resource/role with integration owner.
404team_not_found / invite_not_foundSelected record absent.Check intended team/invite record IDs.
401invite_access; This invitation is not available to your team!Invite belongs to another team.Use the intended matching team/invite pair.
200result:falseModel deletion failed.Ask integration owner to inspect dependent state/constraints before another attempt.
UnspecifiedNo dedicated deletion catchDeletion throws or other handling fails.Reconcile invite and dependent state; no cascade/access-removal promise.

Next task

Inspect intended invite list; handle member relationships as a separate task. Invite deletion is not a refund or account deletion.

Request an invitation resend

POST /api/v1/user/teams/{team_id}/invites/{invite_id}/resend

Attempt the individual_in_team_invite queue event for the stored recipient, with no invitation edit or activation.

Before you call

Use the shared resend context and effects.

Request

No action body is consumed.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended managed team ID.
invite_idpathrequired digitsInvite record ID from create/list; not redeemable code.
token, resourceheadersexisting contextMember identity and selected resource/eligibility/event context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultboolean trueAction completed its queue-attempt path; not mail delivery or invite validity.

Example: Request another notification for invite 3001

Select the intended existing team/invite pair. The stored recipient reader@example.com is used; no new body email/code is accepted.

curl -X POST "${WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001/resend" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/invites/3001/resend`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/invites/3001/resend",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "result": true
}

Consequential alternate

Invite lookup missing gives HTTP 404 excerpt (other metadata omitted):

{
  "error": "invite_not_found",
  "error_description": "Invite not found"
}

A swallowed queue transmission exception can instead still produce result:true. Neither response proves delivery.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMember context fails.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check intended team/Plan/resource/role with integration owner.
404team_not_found / invite_not_foundSelected record absent.Check intended team/invite record IDs.
401invite_access; This invitation is not available to your team!Invite belongs to another team.Use the intended matching team/invite pair.
200result:true despite swallowed queue failureQueue transmission exception logged internally.Reconcile notification handling with integration owner; avoid blind resend loops.
UnspecifiedNo dedicated resend catchOther context/queue handling failure.Inspect selected records/context with integration owner before another send attempt.

Next task

Read invite detail for stored recipient/code. Invitee Group-memberships (teams) management remains separate; resend does not reserve a seat or reset usage.

Resend through the admin-prefixed registration

POST /api/v1/admin/teams/{team_id}/invites/{invite_id}/resend

Attempt the individual_in_team_invite queue event for the stored recipient, with no invitation edit or activation.

Before you call

Use the shared resend context and effects.

This admin-prefixed route uses the same api Teams.resendInvite action and user ACL grant, not a separate admin-only action. Prefix changes credential lookup: an existing token can resolve without resource-scoped session selection before controller initialization; resource-key admin context is a separate existing mechanism. Configured Firebase resolution can still apply in this api controller. Supply the intended resource header for eligibility/event context. Use only an existing authorized token; no key creation is part of this route.

Request

No action body is consumed.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended managed team ID.
invite_idpathrequired digitsInvite record ID from create/list; not redeemable code.
token, resourceheadersexisting contextMember identity and selected resource/eligibility/event context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultboolean trueAction completed its queue-attempt path; not mail delivery or invite validity.

Example: Use the same resend action with admin-prefix context

Select the intended existing team/invite pair. The stored recipient reader@example.com is used; no new body email/code is accepted.

curl -X POST "${WALLKIT_API_BASE}/api/v1/admin/teams/1001/invites/3001/resend" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/admin/teams/1001/invites/3001/resend`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/admin/teams/1001/invites/3001/resend",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "result": true
}

Consequential alternate

Invite lookup missing gives HTTP 404 excerpt (other metadata omitted):

{
  "error": "invite_not_found",
  "error_description": "Invite not found"
}

A swallowed queue transmission exception can instead still produce result:true. Neither response proves delivery.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failMember context fails.Follow credential recovery.
401incorrect_dataAction eligibility fails.Check intended team/Plan/resource/role with integration owner.
404team_not_found / invite_not_foundSelected record absent.Check intended team/invite record IDs.
401invite_access; This invitation is not available to your team!Invite belongs to another team.Use the intended matching team/invite pair.
200result:true despite swallowed queue failureQueue transmission exception logged internally.Reconcile notification handling with integration owner; avoid blind resend loops.
UnspecifiedNo dedicated resend catchOther context/queue handling failure.Inspect selected records/context with integration owner before another send attempt.

Next task

Read invite detail for stored recipient/code. Invitee Group-memberships (teams) management remains separate; resend does not reserve a seat or reset usage.

List non-owner team members

GET /api/v1/user/teams/{team_id}/users

Page users joined through non-owner relationships; inactive/removed/suspended relationships are not automatically excluded.

Before you call

Use the shared member context and selected-team management eligibility. The selected team is looked up by ID without resource filtering.

Owners are excluded using role != owner. There is no resource/global-user-active/relationship-status/expiry filter beyond supplied metadata filter. List items use full user without resource argument and team-scoped membership summaries; the requested team_relationship expansion is disabled, so no selected-team role/status/team_relationship expansion is returned. Conditional full-user admin role retains its separate account meaning. Do not infer those fields from team-detail users.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID.
token, resourceheadersexisting member/resource contextManager identity and eligibility context.
filterqueryoptional bracket mapUsers/UserTeamRelationships model-field filtering; fixed team and non-owner predicates remain.
byquerystring, default Users.idDirect ordering expression, no strict allowlist.
sortquerystring, default DESCDirect order direction, no strict enum validation.
limitqueryint-filter/cast, default 10Page size; no explicit upper bound.
pagequeryraw cast integerSend 1 explicitly; no explicit default.

Result

HTTP 200 items array and common paginator. Item projection is member-list item, empty [] possible. No active/unexpired membership restriction in team-scoped summary query.

Example: Inspect user 4002 without confusing it with a relationship ID

Read first page of non-owner users for team 1001. The excerpt deliberately has no role/status, which this list does not add.

curl "${WALLKIT_API_BASE}/api/v1/user/teams/1001/users?page=1&limit=10" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/users?page=1&limit=10`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/users?page=1&limit=10",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "items": [
    {
      "id": 4002,
      "email": "reader@example.com",
      "subscriptions": []
    }
  ],
  "paginator": {
    "current_page": 1,
    "page": 1,
    "total_pages": 1,
    "total_items": 1,
    "limit": 10
  }
}

Consequential alternate

If only an owner exists, HTTP 200 excerpt:

{
  "items": []
}

Empty non-owner list does not imply an empty team. Removed/suspended rows can still appear without a status filter.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failManager member context fails.Follow credential recovery.
401incorrect_dataNeither eligibility nor active team admin/owner passes.Check intended Plan/resource/team role with integration owner.
404team_not_foundSelected record missing.Use intended team ID.
200items:[]No selected non-owner rows.Handle empty page; owner rows belong to team detail.
UnspecifiedNo dedicated list catchMetadata/query/serialization failure.Correct filter/order and inspect stored relationships through integration owner.

Next task

Read member detail using user 4002. For the actual selected-team role/status, use team detail, not an invented list field.

Read a member linked to the selected team

GET /api/v1/user/teams/{team_id}/users/{user_id}

Read an existing person only after confirming a relationship to the selected team, regardless of that relationship’s status.

Before you call

Use the shared member context and selected-team management eligibility. The selected team is looked up by ID without resource filtering.

It fetches user by ID without a resource-registration/active check and requires any user/team relationship. A removed/suspended relationship still passes that association check. Returned resource state differs from team relationship state; do not substitute active/locked for team status.

Request

Bodyless request.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID.
user_idpathrequired digitsPerson’s user ID, not team relationship ID.
token, resourceheadersexisting member/resource contextManager identity and eligibility context.

Result

HTTP 200 top-level selected member with resource context. It includes that person’s resource memberships and partner-scoped active teams, not only the selected team. No auth token or last_action sign-in field is added.

Example: Read user 4002’s selected-resource account state

Use the person ID from list/team detail. This excerpt omits resource profile fields and nested membership/team objects.

curl "${WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/users/4002",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 4002,
  "email": "reader@example.com",
  "subscriptions": [],
  "teams": []
}

Consequential alternate

No user/team relationship yields HTTP 401 excerpt (other metadata omitted):

{
  "error": "invite_access",
  "error_description": "This user is not available to your team!"
}

The code name is invite_access, but this error concerns team membership association, not an invite code.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failManager member context fails.Follow credential recovery.
401incorrect_dataNeither eligibility nor active team admin/owner passes.Check intended Plan/resource/team role with integration owner.
404team_not_found / user_not_foundSelected record missing.Use intended team/person IDs.
401invite_accessUser exists but has no selected-team relationship.Check intended team/person association; replacing an invite code does not repair it.
UnspecifiedNo dedicated detail catchQuery/nested account projection failure.Ask integration owner to inspect related account data.

Next task

Choose role/status update, removal or suspension only for the intended relationship change.

Update a team member relationship

PUT /api/v1/user/teams/{team_id}/users/{user_id}

Change supplied role/status, optionally attempt a selected Pricing change. This is not global user suspension or owner reassignment.

Before you call

Use the shared member context and selected-team management eligibility. The team lookup uses its ID without resource filtering.

  • Target: any existing person/team relationship qualifies, including owner, removed or suspended relationships.
  • Role and status: role can become user or admin; status can become active, removed or suspended. There is no last-owner guard, so a role edit can demote the owner. The relationship save is not checked and attempts a narrow cache update.
  • Pricing: if an exact person/team/Pricing membership already exists, supplying subscription_id does not replace it. Otherwise the Pricing checkout path fails with the literal message “Checkout teems is disabled ”, even for a free Pricing. Role/status may already have been saved; this failure has no explicit rollback. A successful new-Pricing grant or purchase is not established.

Without the disabled checkout branch, the transaction commits and returns account state. Role/status edits do not explicitly delete memberships, write history or change sponsorships. Renewal processing skips inactive team relationships; that does not prove immediate content-access revocation or a refund.

Request

JSON object required; omitted/null fields unchanged.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID.
user_idpathrequired digitsPerson’s user ID, not team relationship ID.
token, resourceheadersexisting member/resource contextManager identity and eligibility context.
roleJSONoptional nonempty string: user or adminStored relationship role; owner cannot be assigned through this field. No owner-preservation guard.
statusJSONoptional nonempty string: active, removed, suspendedStored relationship status, not global user state.
subscription_idJSONoptional existing Pricing IDExistence/team linked eligibility/capacity/payment-customer validation; missing exact membership reaches disabled checkout.

Result

HTTP 200 top-level selected member with resource context. It includes that person’s resource memberships and partner-scoped active teams, not only the selected team. No auth token or last_action sign-in field is added.

Read the returned person ID/state, not an assumed durable role/status result: the selected team relationship is not separately serialized.

Example: Change role/status without invoking disabled Pricing checkout

Use a status/role-only request for user 4002. No subscription_id is sent. Intended ordinary success projection can show resource account state while the selected team relationship differs.

curl -X PUT "${WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"role": "user", "status": "active"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002`, {
  method: "PUT", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"role": "user", "status": "active"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'role': 'user', 'status': 'active'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/users/4002",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "id": 4002,
  "email": "reader@example.com",
  "subscriptions": [],
  "teams": []
}

Consequential alternate

If a supplied Pricing has no exact existing user/team membership, HTTP 406 excerpt (other metadata omitted):

{
  "error": "team_error",
  "error_description": "Checkout teems is disabled "
}

A reachable primary success for new Pricing assignment is N/A in this source. Role/status may already have been saved; no atomic rollback or payment guarantee.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failManager member context fails.Follow credential recovery.
401incorrect_dataNeither eligibility nor active team admin/owner passes.Check intended Plan/resource/team role with integration owner.
404team_not_found / user_not_foundSelected record missing.Use intended team/person IDs.
401invite_accessNo selected user/team relationship.Check intended person/team pair.
406incorrect_data; Body must be json formatNo truthy JSON body.Supply object with supported fields.
409invalid_role / invalid_status / invalid_subscription_idSupplied field or Pricing eligibility fails.Use accepted role/status/Pricing and inspect capacity.
406team_error; Checkout teems is disabledMissing exact membership enters disabled checkout.Do not retry as a purchase; reconcile role/status and intended Pricing with owner.
406subscription_error / payment_errorCorresponding caught exception.Reconcile local state; no explicit rollback in these catches.
409user_team; Not caught problemOther caught exception.Ask owner to reconcile relationship state before repeating.

Next task

Inspect team detail for selected role/status and member detail for account state. Do not infer payment or content access.

Mark the selected team relationship removed

DELETE /api/v1/user/teams/{team_id}/users/{user_id}

Set relationship status to removed; keep the relationship/person record rather than deleting the user.

Before you call

Use the shared member context and selected-team management eligibility. Team lookup uses its ID without resource filtering.

Owner restriction: the target must have a person/team relationship. An owner can be changed only if another owner-role relationship exists. That replacement does not have to be active, so the check does not guarantee an active replacement owner.

Save and notification: the save result is checked. The team_owner_removed_user event is attempted only after a true save result. Saving attempts a narrow cache invalidation rather than a full cache refresh.

The action does not explicitly delete membership, write history, clean sponsorships, lock the global account or refund payment. Renewal processing skips inactive team relationships, but this request does not establish immediate content-access revocation.

Request

No action body is consumed; status is fixed to removed.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID.
user_idpathrequired digitsPerson’s user ID, not team relationship ID.
token, resourceheadersexisting member/resource contextManager identity and eligibility context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultbooleanRelationship save return; true does not delete person/account or certify notification delivery.

Example: Remove user 4002 from active team participation

As a permitted team manager, mark user 4002 removed from team 1001. Use the person ID, without a relationship ID or status body.

curl -X DELETE "${WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002`, {
  method: "DELETE", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/users/4002",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_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))

Synthetic HTTP 200 excerpt:

{
  "result": true
}

Consequential alternate

For the only owner-role relationship, HTTP 406 excerpt (other metadata omitted):

{
  "error": "team_user_fail",
  "error_description": "You can not delete the owner of the team, first change the owner."
}

The same literal message is used by suspension. A false save can instead return HTTP 200 result:false; that does not confirm the status change.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failManager member context fails.Follow credential recovery.
401incorrect_dataNeither eligibility nor active team admin/owner passes.Check intended Plan/resource/team role with integration owner.
404team_not_found / user_not_foundSelected record missing.Use intended team/person IDs.
401team_user_fail; This user is not in your team.Selected relationship absent.Check intended team/person pair.
406team_user_fail; You can not delete the owner of the team, first change the owner.Owner has no other owner-role relationship.Ask integration owner to handle intended ownership; this route does not assign owner.
200result:falseRelationship save failed.Reconcile stored status before any repeat.
UnspecifiedNo dedicated save/event catchSave/event handling throws.Inspect relationship/queue state; no safe retry or delivery guarantee.

Next task

Use team detail to inspect stored status. If reactivation is intended, separately consider status update; global identity/content access remains a separate decision.

Suspend the selected team relationship

PATCH /api/v1/user/teams/{team_id}/users/{user_id}

Set relationship status to suspended; keep the relationship/person record rather than deleting the user.

Before you call

Use the shared member context and selected-team management eligibility. Team lookup uses its ID without resource filtering.

Owner restriction: the target must have a person/team relationship. An owner can be changed only if another owner-role relationship exists. That replacement does not have to be active, so the check does not guarantee an active replacement owner.

Save and notification: the save result is checked. The team_owner_suspended_user event is attempted only after a true save result. Saving attempts a narrow cache invalidation rather than a full cache refresh.

The action does not explicitly delete membership, write history, clean sponsorships, lock the global account or refund payment. Renewal processing skips inactive team relationships, but this request does not establish immediate content-access revocation.

Request

No action body is consumed; status is fixed to suspended.

NameLocationType / requirementMeaning / constraint
team_idpathrequired digitsIntended team ID.
user_idpathrequired digitsPerson’s user ID, not team relationship ID.
token, resourceheadersexisting member/resource contextManager identity and eligibility context.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultbooleanRelationship save return; true does not delete person/account or certify notification delivery.

Example: Suspend user 4002’s team participation

As a permitted team manager, suspend user 4002’s participation in team 1001. Use the person ID, without a relationship ID or status body.

curl -X PATCH "${WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/user/teams/1001/users/4002`, {
  method: "PATCH", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/user/teams/1001/users/4002",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_TOKEN"]}, method="PATCH")
try:
    with urlopen(request) as response:
        print(response.status, json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))

Synthetic HTTP 200 excerpt:

{
  "result": true
}

Consequential alternate

For the only owner-role relationship, HTTP 406 excerpt (other metadata omitted):

{
  "error": "team_user_fail",
  "error_description": "You can not delete the owner of the team, first change the owner."
}

The same literal message is used by suspension. A false save can instead return HTTP 200 result:false; that does not confirm the status change.

Recovery

HTTP statusAPI code / shapeCauseNext action
401auth_failed / auth_access_failManager member context fails.Follow credential recovery.
401incorrect_dataNeither eligibility nor active team admin/owner passes.Check intended Plan/resource/team role with integration owner.
404team_not_found / user_not_foundSelected record missing.Use intended team/person IDs.
401team_user_fail; This user is not in your team.Selected relationship absent.Check intended team/person pair.
406team_user_fail; You can not delete the owner of the team, first change the owner.Owner has no other owner-role relationship.Ask integration owner to handle intended ownership; this route does not assign owner.
200result:falseRelationship save failed.Reconcile stored status before any repeat.
UnspecifiedNo dedicated save/event catchSave/event handling throws.Inspect relationship/queue state; no safe retry or delivery guarantee.

Next task

Use team detail to inspect stored status. If reactivation is intended, separately consider status update; global identity/content access remains a separate decision.

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