Documentation

Building a Membership Program

Define membership tiers, grant entitlements, gate events and rewards on them, and act on renewals.

This guide builds a membership program end to end: define the tiers, decide what each one grants, enrol people, gate an event and a reward on those grants, and act on renewals. Everything here needs an organization-scoped token.

A membership here is enrolment in one of your organization's tiers (/organizations/{slug}/memberships). It is separate from staff, a role on the organization's team such as owner or manager (/organizations/{slug}/staff), and from a user's subscription, their own plan with Davi. Someone can hold a paid tier without being staff, or run the organization without holding a tier.

1. Define the tiers

curl -X POST "https://api.davi.social/api/v1/organizations/ORG_SLUG/membership-tiers" \
  -H "Authorization: Bearer ORG_SCOPED_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gold",
    "description": "Priority access and double points",
    "price_cents": 999,
    "payment_interval": "monthly",
    "entitlements": {
      "activity.vip_lounge": null,
      "reward.points_multiplier": 2
    }
  }'
  • name, price_cents and payment_interval are required. price_cents: 0 makes a free tier.
  • payment_interval is daily, weekly, monthly, yearly or one_time.
  • Creating and updating tiers takes org:tiers:manage.

Choose the entitlement keys first

entitlements defines what the program grants. Each key is a capability you gate on later, and you choose the vocabulary.

  • A null value is a boolean capability: the key's presence is the grant.
  • A non-null value carries configuration, such as a multiplier.

Name keys for what they unlock, not who gets them. activity.vip_lounge survives a tier being renamed, split or retired; gold_tier_perk does not. Gates name a key, never a tier, so to give two tiers the same access you grant both the same key. Adding a tier later does not mean revisiting any gate.

The reserved member key is implicit. Every active member holds it whatever their tier. It is stripped if you set it on a tier and never appears in a tier response. Use it as the gate for "any member, any tier".

2. Enrol people

curl -X POST "https://api.davi.social/api/v1/organizations/ORG_SLUG/memberships" \
  -H "Authorization: Bearer ORG_SCOPED_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "user_uuid": "USER_UUID", "membership_tier_slug": "gold" }'

POST .../memberships (scope membership:join) enrols somebody in a tier, named by slug. To upgrade or downgrade, PATCH the member's membership with a new tier (membership:tier:update) rather than cancelling and rejoining.

For a negotiated rate or a comped membership, set custom_tier_price_cents and custom_tier_payment_interval on the membership instead of defining a tier nobody else will hold. Entitlements still come from the tier: a custom price changes what somebody pays, never what they can reach.

A membership is active, expired or cancelled. Only active grants entitlements, so an expired member holds no keys at all, including member.

Claiming a card can enrol somebody. If a card carries a membership tier and its claimant is not already a member, claiming creates the membership. Your program can gain members you never enrolled through this endpoint, so a member count kept in your own records will drift from Davi's.

3. Gate what members reach

Activities, sessions and reward templates each carry one required_entitlement, with the same rules everywhere:

required_entitlementWho passes
nullEveryone
"member"Any active member, at any tier
any other keyOnly members whose tier grants that key

To make an event members-only, set it on the activity or session:

curl -X PATCH "https://api.davi.social/api/v1/activities/ACTIVITY_SLUG" \
  -H "Authorization: Bearer ORG_SCOPED_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "required_entitlement": "activity.vip_lounge" }'

A session inherits from its activity and can override it, so a members-only series can still have one open session for newcomers.

To make a reward members-only, set it on the reward template. Two consequences:

  • The entitlement gate is independent of the ticket gate. A ticket holder must still satisfy it. See Running an Event.
  • A gated reward cannot be issued to somebody without an account, because a wallet with no owner holds no entitlements. Leave the gate unset on rewards you hand out at a door.

4. Read a member's entitlements

One call resolves a member's tier and everything it grants. It takes a client_credentials token belonging to the organization, with membership:entitlements:read; see Feature Gating.

curl "https://api.davi.social/api/v1/organizations/ORG_SLUG/memberships/USER_UUID/entitlements" \
  -H "Authorization: Bearer TOKEN"
{
  "has_membership": true,
  "tier_slug": "gold",
  "entitlements": { "reward.points_multiplier": 2, "activity.vip_lounge": null, "member": null }
}

Someone with no membership gets has_membership: false and empty entitlements, not an error.

The resolved set includes the reserved member key, so an "any member, any tier" check is a lookup. For a single key, GET .../entitlements/{key} answers granted directly. Feature Gating covers both, and which tokens can call them.

5. Act on joins, renewals and departures

Subscribe your webhook endpoint to the membership events:

EventUse it to
membership.joinedWelcome a new member, issue a joining reward
membership.renewedThank a returning member
membership.leftRevoke anything you granted outside Davi

A membership whose term runs out sends no event, including one that served out a cancellation. Access stops at valid_until; to act on a lapse, read the membership list on a schedule and compare each valid_until with now.

To issue a reward automatically on renewal instead of handling the event yourself, register a reward trigger on the same condition.

Next