Validate and activate an invitation

Preview a supplied code with resource context, then activate it only with the intended member’s identity. A valid preview does not reserve capacity or change team membership.

Actor / taskOperation
Guest or member: preview a codeValidate
Active member: accept the codeActivate

Example clients and synthetic values follow response conventions.

Checks and scope

Both operations require a valid resource key. Validation is case-insensitive within that resource.

  • Usage and time: positive activation caps and configured start/end windows are checked.
  • Email: invitation email and team-domain checks run only when a nonempty comparison email exists. A guest preview without email can pass those checks; it does not prove recipient identity.
  • Reuse: an existing user cannot reuse a team invite. For a non-team Pricing invite, reuse is rejected when the recorded activation is at least 12 hours old, subject to the earlier global cap.
  • Capacity: a positive team member_limit counts all team relationships, including inactive ones.

These checks do not reserve a seat or make concurrent activation atomic.

Preview a supplied invitation

POST /api/v1/invite-validation

Return invite, Pricing, ticket and team context without activating them. The call updates IP/resource/action flood-counter cache; it is not entirely without writes.

Before you call

Guest ACL permits this action with a valid resource header. An optional member token supplies the session email only if no truthy email input is supplied. Read the checks; a caller-supplied email comparison is not verified identity. Rate threshold is 50 in the cached action/resource/IP counter; no fixed reset window promised.

Request

Prefer JSON. If the decoded JSON is empty or otherwise false in a boolean check, the handler uses posted form fields.

NameLocationType / requirementMeaning / constraint
resourceheaderrequired public keyScopes validation lookup.
tokenheaderoptional existing member tokenSupplies fallback email; not required for guest preview.
inviteJSON / formrequired code stringPresence and invite validation, then string/trim for final lookup. Form input is trimmed before validation; JSON raw input reaches validator first.
emailJSON / formoptional string / nullTruthy input sanitized email/trim/lower; otherwise session email or null. No separate Email validator on this field.
return_inviteJSON / formoptional truthy flag; form Boolean-filter default falseAdds data record projection. JSON uses truthiness, not strict boolean validation; supply true/false.

Result

HTTP 200 JSON.

FieldType / presenceMeaning
resultboolean trueValidation passed at this point; no reservation/activation.
emailstring / nullEffective comparison email, not a verified ownership claim.
invitepublic inviteDisplay/window/code fields only.
subscriptionpublic Pricing / nullIncluded for truthy invite Pricing ID; no entitlement created.
ticketpublic ticket / nullIncluded for truthy ticket ID; not a purchased pass.
teampublic team with Pricing / nullIncluded for truthy team ID; its subscription may be {}.
datavalidation data; only truthy return_inviteRicher record with is_user_exist; not proof of active sign-in.

Example: Preview the generated team invite for the intended email

Use the returned code from the create scenario with the same resource and recipient email. This excerpt omits nested Pricing/team detail; no token is supplied for this guest preview.

curl -X POST "${WALLKIT_API_BASE}/api/v1/invite-validation" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "Content-Type: application/json" \
  --data-raw '{"invite": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001", "email": "reader@example.com"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/invite-validation`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({"invite": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001", "email": "reader@example.com"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'invite': 'SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001', 'email': 'reader@example.com'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/invite-validation",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "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 200 excerpt:

{
  "result": true,
  "email": "reader@example.com",
  "invite": {
    "title": "Invite for Example Media team",
    "description": "Invite for Example Media team",
    "code": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001",
    "start_date": null,
    "end_date": null
  },
  "ticket": null
}

Validation does not send email or consume an activation.

Consequential alternate

A configured usage cap already reached gives HTTP 409 excerpt (other metadata omitted):

{
  "error": "invalid_invite",
  "error_description": "Invite usage limit has been reached."
}

Do not proceed to activation or infer that a prior valid preview reserved capacity.

Recovery

HTTP statusAPI code / shapeCauseNext action
404resource_not_existsMissing/wrong resource context.Check the supplied resource key.
406requests_limit_exceededCached IP/action/resource limit reached.Stop repeated preview attempts; ask integration owner about counter policy.
409invalid_invitePresence, resource/code, time, cap, email/domain, reuse or team capacity fails.Interpret description and use intended resource/email/code; seek issuer help for expiry/capacity.
406incorrect_invite; Incorrect invitation codeFinal normalized lookup finds no invite after validation.Confirm exact supplied code/resource with issuer; no activation occurred.

Next task

Use the intended member’s identity for activation. If no identity exists, follow the already configured ordinary identity flow; preview does not create it.

Accept the invitation as the intended member

POST /api/v1/invite-activation

Record activation and conditionally attach team/Pricing relationships. It can replace existing memberships; result:true is not a provider payment or content-access decision.

Before you call

The ACL lists this action for guests, but the action requires an active member and resource. Resolve the intended invitee using their existing token/resource context, including configured Firebase handling when applicable. The comparison email comes from that member, not a body field. The invitation checks run again.

Record selection: validation is resource-scoped, but activation then looks up the code without resource scope. Duplicate codes across resources can therefore select a different record.

Writes: the action starts a database transaction, attempts an unchecked activation save, and creates or reactivates a team relationship as active/user. This can overwrite an existing role. It can also set the person’s lock-prevention setting and attempt notification events.

Membership: a team Pricing fallback or free invitation Pricing can create a membership. The paid invitation Pricing branch has no active checkout implementation and can return success without granting that Pricing. The membership helper can replace same-Plan/same-team relationships and write history. With the resource’s single_subscription setting, it can remove other resource memberships and clear or stop associated sponsorships. Pricing, team and invite end dates affect the new membership.

Nested transactions, unchecked saves and external event queues do not guarantee atomic rollback or delivered notifications. No ticket-pass creation branch exists here, even when validation returns ticket information.

Request

Prefer JSON. If the decoded JSON is empty or otherwise false in a boolean check, the handler uses the posted form invite.

NameLocationType / requirementMeaning
resource, tokenheadersrequired valid resource and active member contextResource validation plus intended invitee identity.
inviteJSON / formrequired code stringSame validation rules, but comparison email is current user email; final activation lookup is code-only.

Result

HTTP 200 JSON:

FieldType / presenceMeaning
resultboolean trueActivation path returned and committed; unchecked persistence/provider/access completion not certified.
subscription_idinteger / nullInvite Pricing ID only when invite is not team-linked and has a Pricing ID. Team activation always null, even if a team membership was added.

Example: Accept team invite without misreading null Pricing ID

Use the same code and resource after resolving reader@example.com as the active member. With a free eligible Pricing, the ordinary path attaches the team relationship/membership.

curl -X POST "${WALLKIT_API_BASE}/api/v1/invite-activation" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"invite": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001"}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/invite-activation`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"invite": "SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001"})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'invite': 'SYNTHETIC-INVITE-CODE-DO-NOT-USE-0000000001'}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/invite-activation",
    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 200 excerpt:

{
  "result": true,
  "subscription_id": null
}

The null value follows team-linked output rules; it does not by itself mean no membership. Read resulting account state and separately decide content access.

Consequential alternate

Reusing this team invitation as the same known user can return HTTP 422 excerpt (other metadata omitted), when the global cap has not already rejected it first:

{
  "error": "invalid_invite",
  "error_description": "You have already used this invitation."
}

For the generated one-use example, the global usage-limit message normally wins once one activation exists. Neither result authorizes repeated acceptance.

Recovery

HTTP statusAPI code / shapeCauseNext action
404resource_not_existsMissing/wrong resource.Check code issuer’s resource key.
401auth_failed / auth_access_failActive invitee identity not resolved.Use the intended member’s existing sign-in/context.
422invalid_inviteValidation fails, including cap/window/email/reuse/team capacity.Correct context/input or ask issuer to inspect state; do not reuse blindly.
406team_invitation_fail / incorrect_subscriptionTeam/Pricing helper exception.Reconcile relationship/activation state with integration owner before retry.
406team_payment_failPaymentException catch exists; no active team checkout branch here.Do not infer charged/refunded state; ask owner to inspect cause.
406accept_invite_failInvite or other caught exception.Reconcile partial writes/events; no universal rollback or safe retry promise.

Next task

Read User subscriptions and Pricing selection, then request the separate content-access decision when serving content. See Group-memberships (teams) management.

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