Skip to main content
Member management is server-side. These calls use the secret Org-API-Key on your backend only - never ship it in an app or client code. A request with a member token is rejected with 403. See Authentication and Server-side API.
A member is one of your users as Expys knows them, keyed by your own stable externalUserID. From your backend you upsert a member’s profile and tier, read a full summary of their wallet and redemption activity, and remove them when needed.

Upsert a member

setMember(externalUserID, {...}) calls PUT /v1/members/{externalUserID} and creates the member if they do not exist or updates them if they do. PUT is idempotent by HTTP semantics, so replaying the same body is safe.
displayName
string
Optional display name stored on the member profile.
tier
string
A free-form tier string you define (for example gold, vip). It flows into member-mode eligibility to gate offers.
attributes
object
Free-form JSON for your own metadata. Stored as-is and returned on getMember.
curl
curl -X PUT https://api.expys.com/v1/members/user_123 \
  -H "Authorization: Bearer YOUR_ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Ada Lovelace",
    "tier": "gold",
    "attributes": { "plan": "annual", "region": "eu" }
  }'
The response is a SetMemberResponse:
FieldTypeDescription
externalUserIDstringYour stable identifier for the member.
displayNamestring | nullThe stored display name, or null if unset.
tierstringThe member’s tier.
attributesobject | nullYour free-form metadata, or null if unset.
tier is a string you define - Expys does not enforce a fixed set. Keep tier names consistent with the conditions you check in eligibility.

Read a member summary

getMember(externalUserID) calls GET /v1/members/{externalUserID} and returns a MemberSummary: the profile plus the member’s wallet and a per-status count of their redemptions.
curl
curl https://api.expys.com/v1/members/user_123 \
  -H "Authorization: Bearer YOUR_ORG_API_KEY"
externalUserID
string
required
Your stable identifier for the member.
displayName
string
The stored display name, or null if unset.
tier
string
required
The member’s tier.
attributes
object
Your free-form metadata, or null if unset.
wallet
Wallet
required
The member’s points wallet.
redemptionCounts
RedemptionCounts
required
A map of redemption status to count for this member.

Wallet

FieldTypeDescription
balancenumberCurrent points balance.
amountReceivednumberTotal points ever credited.
amountSpentnumberTotal points ever spent.
currency.namestringDisplay name of the points currency.
currency.symbolstringSymbol of the points currency.

Redemption counts

redemptionCounts is a per-status count of the member’s redemptions across the booking lifecycle:
KeyMeaning
SUBMITTEDRedemptions submitted.
OPENRedemptions in the open state.
AWAITING_VENDORWaiting on the vendor.
AWAITING_CUSTOMERWaiting on the customer.
PURCHASEDPurchased (sometimes called confirmed).
COMPLETEDCompleted experiences.
CANCELEDCanceled redemptions.
All values are numbers.

Remove a member

removeMember(externalUserID, { retainBalance? }) calls DELETE /v1/members/{externalUserID} and archives the member. The optional retainBalance query parameter controls whether the points balance is kept.
retainBalance
boolean
When true, the member’s points balance is retained on the archived record. When omitted or false, the balance is not retained.
curl
curl -X DELETE "https://api.expys.com/v1/members/user_123?retainBalance=true" \
  -H "Authorization: Bearer YOUR_ORG_API_KEY"
The response is a RemoveMemberResponse:
FieldTypeDescription
externalUserIDstringThe member that was removed.
archivedbooleantrue when the member was archived.
balanceRetainedbooleanWhether the points balance was kept (reflects retainBalance).
Removal archives rather than hard-deletes the member, so historical redemptions and analytics remain consistent.

In code

The server client is constructed with the Org-API-Key. The example below upserts a member; getMember and removeMember are called the same way.
import { initialize } from "@expys/sdk";

const orgApiKey = process.env.EXPYS_ORG_API_KEY;
if (!orgApiKey) {
  throw new Error(
    "Set EXPYS_ORG_API_KEY (your secret Org-API-Key, e.g. expys_live_...). " +
      "Run this on a backend only, never in a client app.",
  );
}

// Construct the client with the machine credential as the token. Server-mode
// methods are guarded against member tokens client-side.
const expys = initialize({
  baseUrl: process.env.EXPYS_BASE_URL,
  environment: "sandbox",
  token: orgApiKey,
});

const externalUserID = process.env.EXPYS_EXTERNAL_USER_ID ?? "user_42";

async function main(): Promise<void> {
  // Mint a short-lived member token for your app to use (return this to the app,
  // never the Org-API-Key). Idempotent POST: a retry replays rather than re-mints.
  const grant = await expys.exchangeToken({ externalUserID });
  console.log(`minted member token expiring at ${grant.expiresAt}`);

  // Upsert the member's profile. PUT is idempotent by HTTP semantics (no key).
  const member = await expys.setMember(externalUserID, {
    displayName: "Ada Lovelace",
    tier: "gold",
  });
  console.log(`member ${member.externalUserID} is now tier=${member.tier}`);

  // Credit points to the member's wallet. Idempotent POST sends an Idempotency-Key.
  const credited = await expys.creditPoints({
    amount: 100,
    externalUserID,
    reason: "welcome bonus",
  });
  console.log(`new balance: ${credited.balance} ${credited.currency.symbol}`);

  // Register a webhook. The signingSecret is shown ONLY on creation - store it now.
  const webhook = await expys.createWebhook({
    events: ["redemption.created"],
    url: "https://example.com/expys/webhooks",
  });
  console.log(`webhook ${webhook.id} secret: ${webhook.signingSecret}`);

  // Org-wide analytics rollups.
  const summary = await expys.analyticsSummary();
  console.log(
    `members: ${summary.memberCount}, minted: ${summary.pointsMinted}`,
  );
}

Next steps

Eligibility

How a member’s tier gates which offers they can redeem in member mode.

Points and wallet

Mint and credit points into the wallet returned by getMember.