Skip to content

Example: Membership system

This guide walks through building a complete membership flow using workflows. By the end, you'll have automated:

  1. Generating a unique member ID on first purchase
  2. Setting a membership expiry date
  3. Sending a welcome email with the member code
  4. Sending a renewal reminder with a signed identity link
  5. Handling renewals safely — updating the correct member's record even if the email gets forwarded
  6. Handling cancellations through a verified confirmation form
  7. Revoking access when the membership expires

Prerequisites

Before starting, create these custom contact properties in your CRM:

  • Member ID (Text) — stores the unique membership code
  • Membership Status (Select One) — options: Active, Expired, Cancelled, Pending
  • Membership Expiry (Date) — when the membership expires

Read Identity Links first if you haven't — workflows 2, 3, and 5 depend on it.

Smart-value tokens in email bodies — don't type them by hand

The example email bodies below show tokens like {{contact.property.<Member ID property id>}} and {{action.set-custom-property.<Member ID property id>.value}}. The angle-bracketed parts are placeholders — they resolve to the internal property ID (a 24-character hex string), not the human-readable property name. You can't type "Member ID" or "Membership Expiry" in place of them.

To insert a working token in an email body:

  1. Click into the email body where you want the value.
  2. Open the smart-value picker in the sidebar.
  3. Search for the property or action output you want (e.g. "Member ID" or "Membership Expiry" for properties; the Set Contact Property action's output for action values).
  4. Click the matching entry — the picker copies the real token, with the correct internal ID baked in, into your cursor position.

Tokens shown WITHOUT angle brackets — {{contact.firstName}}, {{contact.email}}, {{signed.identity.email}}, and user-named action outputs like {{action.generate-identity-link.renewal-link.url}} — resolve literally and can be typed or copied verbatim.


Workflow 1: New member onboarding

Goal: When a new customer purchases a membership product (through the public website, not via a renewal link), generate a member ID, set their status to Active, set an expiry date, and send a welcome email.

Step 1: Create the workflow

  1. Go to Dashboard > Workflows > Create Workflow.
  2. Name: New Member Onboarding
  3. Occurrence: Persistent (runs for every purchase)

Step 2: Add the trigger

  1. Click Choose a trigger.
  2. Select Product Purchased from the Products category.
  3. Set scope to Specific product and select your membership product.
  4. Expand the Advanced section at the bottom and set Identity link to Non-verified Links Only.

The Non-verified filter means this workflow only runs for public, first-time purchases — not for anyone arriving through a signed renewal link. That's what keeps it from firing on existing members renewing through the email reminder flow.

Membership trigger

Step 3: Generate a Member ID

  1. Click Add action and select Set Contact Property.
  2. Target property: Member ID
  3. Contact email: {{contact.email}}
  4. Value source: A generated code
  5. Value: Member ID

This generates a unique 8-character code like K7M3Q9X2 and writes it to the contact's Member ID property.

Generate member ID

Step 4: Set membership status

  1. Click Add action and select Set Contact Property.
  2. Target property: Membership Status
  3. Contact email: {{contact.email}}
  4. Value source: A value I type in
  5. Value: Active

Step 5: Set expiry date

  1. Click Add action and select Set Contact Property.
  2. Target property: Membership Expiry
  3. Value source: A date X days from Membership Expiry — enter 365
  4. Contact email: {{contact.email}}

For a brand-new member there is no existing expiry yet. The relative to property source falls back to the moment this action runs, so the expiry lands exactly one year from the purchase date.

Set expiry date

Step 6: Send welcome email

  1. Click Add action and select Send Email.
  2. Recipients: {{contact.email}}
  3. Subject: Welcome to the club, {{contact.firstName}}!
  4. Body:
Hi {{contact.firstName}},

Welcome! Your membership is now active.

Your Member ID: {{action.set-custom-property.<Member ID property id>.value}}
Expires: {{action.set-custom-property.<Membership Expiry property id>.value}}

Keep this code handy — you'll need it for member benefits.

Action output tokens

The {{action.set-custom-property.<Member ID property id>.value}} token references the value generated by the earlier Set Contact Property action — it's the actual member code that was just created for this contact. Copy the real token from the smart-value picker (see the warning at the top of this guide); it uses the property's internal ID, not the name.

Welcome email

Step 7: Activate

Switch the workflow status to Active and save.

Complete flow

Complete onboarding flow


Goal: 30 days before a membership expires, mint a signed renewal link and email it to the member. The link cryptographically binds the destination checkout to the member's email, so whoever clicks through only updates that member's record.

This is the first workflow that uses the Identity Links feature.

Step 1: Create the workflow

  1. Name: Membership Renewal Reminder
  2. Occurrence: Persistent

Step 2: Add the trigger

  1. Select Days Before/After Contact Date.
  2. Property: Membership Expiry
  3. Timing: Days before the date
  4. Days: 30

Renewal reminder trigger

  1. Click Add action and select Generate Identity Link.
  2. Name: renewal-link (this becomes part of the output token — keep it short and descriptive)
  3. Target URL: your renewal checkout page, e.g. https://your-checkout.com/productset/<renewal-product-id>
  4. Recipient email: {{contact.email}} — the email the link will identify at the other end
  5. TTL days: 60 — the link stays valid for 60 days, enough to cover the reminder window plus a grace period

Generate Identity Link action configured for membership renewal: Name=renewal-link, Target URL=renewal checkout, Recipient={{contact.email}}, TTL=60

  1. Add Send Email action.
  2. Recipients: {{contact.email}}
  3. Subject: Your membership expires in 30 days
  4. Body:
Hi {{contact.firstName}},

Your membership ({{contact.property.<Member ID property id>}}) expires on
{{contact.property.<Membership Expiry property id>}}.

Renew now to keep your member benefits:
{{action.generate-identity-link.renewal-link.url}}

The <Member ID property id> and <Membership Expiry property id> bits are placeholders — swap them out by searching the smart-value picker for those properties and clicking the match (see the warning at the top of this guide). The {{contact.firstName}} and {{action.generate-identity-link.renewal-link.url}} tokens resolve literally and can stay as-typed.

The {{action.generate-identity-link.renewal-link.url}} token resolves to the full signed URL — the checkout page plus a ?t=<token> query parameter. In a rich-text email you'd typically wrap this in a Renew Now button.

Renewal reminder email preview with "Renew now" link pointing to the signed URL (?t=... token visible)

Step 5: Activate

Switch to Active and save.

Why this setup matters for the next workflow

The signed link carries alice@example.com baked into the token. If Alice forwards the email to her husband Bob and he clicks through and pays, the backend still sees Alice's identity on the verified trigger params. Your renewal workflow (Workflow 3, below) uses that verified email to update the right contact.


Workflow 3: Identity-verified renewal

Goal: When someone purchases the membership product through a signed renewal link, extend the identified member's expiry — not the checkout-typed email's expiry. Send a renewal confirmation to the identified member.

This workflow shares a trigger (same product) with Workflow 1 (onboarding) but splits cleanly on identity state.

Step 1: Create the workflow

  1. Name: Membership Renewal
  2. Occurrence: Persistent

Step 2: Add the trigger

  1. Select Product Purchased → your membership product (same one Workflow 1 uses).
  2. Expand the Advanced section and set Identity link to Verified Links Only.

This makes the workflow only fire when the purchase POST carried a valid signed link — i.e. the purchaser came through a renewal email. Public first-time purchases skip this workflow and fall through to Workflow 1.

Product Purchased trigger with Specific product selected and Advanced → Identity link set to "Verified Links Only"

Step 3: Extend the expiry (on the identified member)

  1. Add Set Contact Property.
  2. Target property: Membership Expiry
  3. Value source: A date X days from Membership Expiry365
  4. Contact email: {{signed.identity.email}} ← the verified email, not {{contact.email}}

This adds 365 days on top of the member's existing expiry — an early renewal keeps their remaining time. Using {{signed.identity.email}} is what prevents the forwarded-email problem: even if the checkout was typed with a different email, the update targets the original member.

Don't use {{contact.email}} here

{{contact.email}} resolves to whatever was typed at checkout. If Bob pays for Alice's renewal using his own email, {{contact.email}} is bob@example.com and your update writes to the wrong contact. {{signed.identity.email}} is the email baked into the signed link — always the correct target.

Step 4: Reactivate the membership status

  1. Add Set Contact Property.
  2. Target property: Membership Status
  3. Contact email: {{signed.identity.email}}
  4. Value source: A value I type in
  5. Value: Active

This handles the case where a member renews after their status has already been flipped to Expired by Workflow 6 (the expiry-day workflow). Without this step, a lapsed member would pay to renew, get the correct new expiry date, but still be marked Expired — and any benefits gated on Membership Status == Active would stay off until manually fixed.

For an early renewal where the status is already Active, this is a no-op.

Step 5: Send renewal confirmation

  1. Add Send Email.
  2. Recipients: {{signed.identity.email}} (again — send to the identified member, not the checkout-typed email)
  3. Subject: Your membership has been renewed!
  4. Body:
Hi,

Your membership has been extended. New expiry date:
{{action.set-custom-property.<Membership Expiry property id>.value}}

Your Member ID remains: {{contact.property.<Member ID property id>}}

The <Membership Expiry property id> and <Member ID property id> bits are placeholders — swap them out by searching the smart-value picker for those properties and clicking the match (see the warning at the top of this guide). {{signed.identity.email}} resolves literally and can stay as-typed.

Step 6: Activate

Switch to Active and save.

Complete flow

Complete Membership Renewal workflow: Product Purchased (Verified Only) → Extend Expiry → Reactivate Status → Send Email, all keyed to {{signed.identity.email}}


Goal: When a member requests to cancel (via a "Cancel my membership" form on your site, a support request, or a webhook), mint a signed confirmation link and email it. The cancellation only takes effect when they submit the confirmation form through the signed link.

This two-step pattern prevents anyone who guesses the cancellation URL — or who has a member's email open — from cancelling without an explicit verified confirmation.

Prerequisite: create the confirmation form and copy its URL

Before building this workflow, create the form the confirmation email will point at. You'll need its URL for Workflow 4 and you'll pick the form itself from the trigger dropdown in Workflow 5, so set it up first.

  1. In the Forms builder, create a simple confirmation form — a single yes/no "Are you sure?" field and a submit button. Save it.
  2. Go to Dashboard → Forms.
  3. In the forms overview table, find the row for your new form and copy its form URL directly from the table.

Hang on to this URL — you'll paste it into the Target URL field of the Generate Identity Link action (Step 2 below). In Workflow 5 you won't need the URL again; you'll pick the form from the trigger's form dropdown.

Step 1: Create the request workflow

  1. Name: Membership Cancellation Request
  2. Occurrence: Persistent
  3. Trigger: Form Submitted on your "Cancel my membership" form (a separate intake form, not the confirmation form from the prerequisite).
  1. Add Generate Identity Link.
  2. Name: cancel-confirm
  3. Target URL: paste the confirmation form URL you copied in the prerequisite above.
  4. Recipient email: {{contact.email}}
  5. TTL days: 7 — short window; cancellation shouldn't linger

Step 3: Send confirmation email

  1. Add Send Email.
  2. Recipients: {{contact.email}}
  3. Subject: Confirm your membership cancellation
  4. Body:
Hi {{contact.firstName}},

We received a request to cancel your membership. To confirm, click
the link below within 7 days:

{{action.generate-identity-link.cancel-confirm.url}}

If you didn't request this, you can safely ignore this email —
your membership will stay active.

Step 4: Activate

Cancellation Request workflow: Intake form submitted → Generate Identity Link (cancel-confirm, TTL=7) → Send confirmation email


Workflow 5: Cancellation — verified form submission

Goal: When the confirmation form is submitted via a valid signed link, mark the identified member's status as Cancelled and notify them. Direct visits to the confirmation form do nothing.

Step 1: Create the workflow

  1. Name: Membership Cancellation
  2. Occurrence: Persistent

Step 2: Add the trigger

  1. Select Form Submitted → the confirmation form you created in Workflow 4 Step 1.
  2. Expand Advanced and set Identity link to Verified Links Only.

Only signed-link submissions fire this workflow. A user who lands on the confirmation form directly (without clicking through a cancellation email) can't trigger a cancellation.

Step 3: Mark status as Cancelled

  1. Add Set Contact Property.
  2. Target property: Membership Status
  3. Value source: Literal > Cancelled
  4. Contact email: {{signed.identity.email}} — the member the link was minted for

Step 4: Send cancellation confirmation

  1. Add Send Email.
  2. Recipients: {{signed.identity.email}}
  3. Subject: Your membership has been cancelled
  4. Body:
Hi,

Your membership has been cancelled. You'll keep access until your
current expiry date:
{{contact.property.<Membership Expiry property id>}}

Sorry to see you go — you're welcome back any time.

The <Membership Expiry property id> bit is a placeholder — swap it out by searching the smart-value picker for the property and clicking the match (see the warning at the top of this guide). {{signed.identity.email}} resolves literally and can stay as-typed.

Step 5: Activate

Switch to Active and save.

Complete cancellation flow

Complete Membership Cancellation workflow: Form Submitted (Verified Only) on confirmation form → Set Status=Cancelled → Send goodbye email, all keyed to {{signed.identity.email}}


Workflow 6: Expiry day — revoke access

Goal: On the membership expiry date, set status to Expired and notify the contact.

Step 1: Create the workflow

  1. Name: Membership Expired
  2. Occurrence: Persistent
  3. Trigger: On Contact Date PropertyMembership Expiry

Step 2: Set status to Expired

  1. Add Set Contact Property — set Membership Status to Expired.
  2. Contact email: {{contact.email}}

Step 3: Send expiry notification

  1. Add Send Email.
  2. Subject: Your membership has expired
  3. Body:
Hi {{contact.firstName}},

Your membership expired today. To continue enjoying member benefits,
please renew your membership.

Step 4: Activate

Switch to Active and save.

Member IDs, discounts, and fully revoking access

A common pattern is to sync the Member ID as a property-based discount code — members get automatic member-only pricing at checkout because their contact record carries a Member ID the discount rule reads. If you're doing that, setting status to Expired on its own won't stop the discount: the rule looks at the ID being present, not at the status field.

If you want the expiry workflow to revoke pricing benefits as well, add a Delete Contact Property action here that clears the Member ID. Two tradeoffs to know about before you do:

  • Workflow 3 (Renewal) references {{contact.property.<Member ID property id>}} in the confirmation email. An ex-member who renews after expiry will see that field render empty. Either update the email copy to handle the empty case or drop that line.
  • Workflow 1 (Onboarding) will mint a brand-new Member ID for a returning ex-member who re-purchases through the public website — their old ID is gone, so historical reporting keyed on it won't link to the new record.

If continuity of identity matters more than revoking pricing, leave Member ID in place and gate all member-only benefits on Membership Status == Active instead of on ID presence.


Summary of the membership system

Workflow Trigger Identity link What it does
New Member Onboarding Product Purchased Non-verified Only Generates Member ID, sets status + expiry, sends welcome — only for new public purchases
Renewal Reminder 30 days before expiry Mints a signed renewal link, sends reminder email
Membership Renewal Product Purchased Verified Only Extends the identified member's expiry, resets status to Active (covers post-expiry renewals), sends confirmation
Cancellation Request Form Submitted (intake) Mints a signed confirmation link, sends cancellation email
Cancellation Form Submitted (confirm) Verified Only Marks identified member as Cancelled, sends goodbye
Membership Expired On Contact Date (Expiry) Sets status to Expired, sends expiry notification

Together, these six workflows create a fully automated, identity-safe membership lifecycle:

Membership lifecycle: purchase → onboarding → reminder → renewal (verified) → expiry, with a parallel cancellation request → verified cancellation path

The identity link is the load-bearing part

Without the verified-only filter on Workflows 3 and 5, forwarded emails, shared browsers, or guessed URLs could let the wrong person modify the wrong member's record. The verified-only trigger option combined with {{signed.identity.email}} on every downstream action is what makes the system safe.