Events & Shops
How to configure the Flourish Liminal managed package for paid tickets, donations, products, and bookable time slots.
Overview
Flourish Liminal is a Salesforce managed package that gives you a flexible, lightweight way to run any of these:
- Ticket sales for an event, with quantity limits (e.g. 50 general admission seats).
- Product purchases with stock counts.
- Time-slot booking — visitors pick a date/time from a calendar (think appointment booking, classes, tours).
- Paid registrations with online card or bank payment via Braintree.
- Recurring donations if you have NPSP installed.
Every purchase produces an Opportunity in Salesforce, attached to a matched or newly created Contact (and Account, if you have NPSP).
This guide covers everything except the volunteer signup flow. For that, see Volunteers. The two are independent — you can use either, both, or neither.
Before You Start
You need:
- The Flourish Liminal managed package installed.
- A Braintree account (production or sandbox) if you want to take payments.
- Permission to create custom metadata records, edit Lightning pages, and manage permission sets and sharing rules.
- (Optional) NPSP for the recurring-donation features. NPSP is detected automatically; without it, the recurring path is silently skipped.
Step 1 — Configure the org-wide settings
Liminal reads a single custom metadata record to find its Braintree credentials and other defaults.
- Go to Setup > Custom Metadata Types > FL Liminal > Manage Records.
- Click New and fill in:
- Label and FL Liminal Name:
global(case-sensitive — the package looks upgetInstance('global')). - Braintree Token — paste your Braintree API key (Bearer-format).
- Braintree Sandbox Mode — check this for testing; uncheck for production. The package routes all callouts to the sandbox or production endpoint based on this flag.
- Completed Order Opp StageName — the Opportunity StageName Liminal should write when a payment clears (e.g.
Closed Won). Defaults toClosed Wonif blank. - Customer Support Email — shown on the checkout page so customers know who to contact.
- Payment Mailing Address — shown on the checkout page as the "mail a check" address.
- Label and FL Liminal Name:
- Save.
Note: The package errors out with "No org-wide settings have been configured for Flourish Liminal" if this record is missing. You only ever need one record, named
global.
Step 2 — Assign the permission set
The package ships with Liminal Admin, which grants read/edit on every Liminal custom field plus object-level permissions on the three custom objects.
- Go to Setup > Permission Sets.
- Open Liminal Admin > Manage Assignments > Add Assignments.
- Add yourself and anyone else who will be configuring offerings.
Note: This permission set does not grant guest user access. For public-facing pages, see Step 7.
Step 3 — Plan your data: engagements, offerings, availabilities
Liminal's three building blocks:
| Concept | Salesforce object | Think of it as |
|---|---|---|
| Engagement | Campaign | The event, course, or campaign that groups everything (e.g. Spring Gala 2026, Yoga Classes — Summer). |
| Offering | Liminal Offering | A thing within the engagement that someone can buy or book (e.g. General Admission, VIP Ticket, Drop-in Yoga Class). |
| Availability | Liminal Availability | The inventory or schedule for an offering. Three flavors below. |
The Availability record type decides what the offering behaves like:
- Stock — fixed-quantity inventory. Use for tickets, products, anything counted.
- Slot — scheduled time blocks the visitor picks from a calendar. Use for appointments, classes, tours.
- Hybrid — both. A class with 12 seats on a specific Saturday from 10 a.m. to noon.
Step 4 — Create an engagement (Campaign)
- Campaigns tab > New.
- Fill in:
- Campaign Name — public title.
- Active — must be checked for the engagement to appear in the storefront.
- Description, Start Date, End Date — standard.
- Event Image URL — public URL to a hero image.
- Address Street / City / State / Postal Code — display fields.
- Lim Status, Lim Tags — free-form metadata you can use for reporting filters.
- Lim TZ Name / Lim TZ Offset — informational fields for the engagement's timezone.
- Lim Customer Detail Template — optional template string controlling which fields the customer-details form on checkout renders. Leave blank to use the default form.
- Save.
Step 5 — Create offerings and availabilities
5.1 Stock-style offering (tickets, products)
- From your Campaign, scroll to Liminal Offerings > New.
- Fill in:
- Name (internal) and Label (public — leave blank to mirror Name).
- Description (rich text supported).
- Image Landing / Thumbnail — public URLs.
- Price — per-unit price in your default currency. Leave blank for free.
- Engagement — auto-set from the Campaign.
- Save the offering.
- From the offering, create a Liminal Availability with record type Stock:
- Quantity — total available (e.g.
100). - Save.
- Quantity — total available (e.g.
Now 100 units of inventory are available. The package automatically decrements as people add to cart (Claimed) and as orders complete (Ordered). You can create more Stock Availabilities under the same Offering and the system sums them — useful when you receive shipments in batches.
5.2 Slot-style offering (appointments, classes)
- Create the offering as above. Set Slot Value Type =
Date(whole-day slots) orDatetime(with hours). Set Slot TZ (e.g.America/New_York) and Slot Duration if you want fixed-length slots (in minutes for Datetime mode, days for Date mode). - Allow Multiple Slots — check if a single visitor can book several slots in one order.
- Weekend Availability — set to
Unavailableto auto-skip Sat/Sun in the calendar. - Save.
- Create Liminal Availability records with record type Slot:
- Slot Open/Close:
Openfor windows when bookings can happen,Closefor blackouts (holidays, lunch breaks). - Start Datetime / End Datetime — the window. Both must fall inside the Campaign's Start/End dates (a validation rule enforces this).
- Max Bookings — how many overlapping bookings the window can hold.
- Slot Open/Close:
- Save each.
The calendar component subtracts the Close windows from the Opens, then emits fixed-length slots of Slot_Duration__c minutes within whatever remains. Already-booked selections from other orders are also subtracted, so visitors don't see slots that are full.
5.3 Hybrid offering
Create an Availability with record type Hybrid and fill in both Quantity__c and the slot Start/End times. Useful for "a workshop with 12 seats on this exact date."
Step 6 — Publish the storefront page
Liminal ships three main public LWCs you can mix and match on a Lightning App Page or Experience Cloud page:
| Component (in App Builder) | What to use it for | Required properties |
|---|---|---|
| Liminal: Ticket Storefront | Stock-style purchasing UI with +/- buttons. | engagementId, orderId |
| Liminal: Calendar | Slot-style booking UI with calendar grid. | offeringId, orderId |
| Liminal: Checkout | Final payment + customer-details step. | orderId |
A typical end-to-end purchase page looks like one of:
- Tickets: Storefront → Checkout
- Slot booking: Calendar → Checkout
- Mixed: Storefront + Calendar (multiple offerings) → Checkout
To publish:
- Open Lightning App Builder > New Page (App Page or Community Page).
- Drag the components on. In each component's properties panel, set the
engagementId/offeringIdand a sharedorderId. TheorderIdis a free-form string — typically you generate it client-side or use a Flow that sets it before opening the page. - Save and Activate the page.
Generating the Order ID
The Order ID is the cart key — every Selection and the eventual Opportunity share it. Approaches:
- Hardcode for testing: paste any string (
TEST-123) into the component property. - URL parameter: pass
?orderId=...and bind it via a small wrapper LWC or a Flow. - Auto-generate via Flow: a screen flow that calls
Crypto.generateAesKeyor any random-string generator before opening the storefront. The volunteer flow does this server-side; for the storefront you typically do it client-side or in the URL.
A single visitor session should keep the same Order ID across Storefront → Calendar → Checkout — that is what stitches selections into one cart.
Step 7 — Configure Experience Cloud (public sites)
If your storefront lives on a public site rather than inside Salesforce:
- Site published with the page hosting the Liminal components.
- Guest User Profile for the site grants:
- Read on Campaign, Liminal Offering, Liminal Availability.
- Create / Edit / Delete on Liminal Selection.
- Create / Edit on Contact, Opportunity, and Account.
- Sharing rules / sharing sets that expose your active Campaigns, Offerings, and Availabilities to the guest user. (Default org-wide is Private for most of these objects.)
- CSP Trusted Sites — the package adds
FL_ArqueraLiminal(legacy asset host). If you serve images or scripts from other domains, add them in Setup > CSP Trusted Sites. - Remote Site Settings — the package ships
FL_Braintree(production) andFL_BraintreeSandbox; both should be Active. They authorize the server-to-Braintree callouts.
Test the full flow in an incognito browser window using a Braintree sandbox card number (e.g. 4111 1111 1111 1111) before going live.
Step 8 — How payments work
The Liminal: Checkout component:
- Loads the order's Selections via the
orderId. - Calls Braintree to get a client token for the embedded payment form (uses
Braintree_Token__c+ the sandbox flag from yourglobalcustom metadata record). - Renders the payment form (card or bank account, depending on what you have enabled).
- On submit, sends the payment nonce + amount to Braintree.
- On success, Liminal:
- Finds-or-creates the Contact (fuzzy match by name + email + phone + address via SOSL; if NPSP is installed, alternate emails are checked too).
- Inserts or updates an Opportunity with
Lim Order ID= your order id, Amount = sum ofPrice Finalacross selections, StageName = the value fromCompleted_Order_Opp_StageName__c(orClosed Wonif blank), CloseDate = today. - Flips all the order's Selections to status Ordered.
If payment succeeds but Salesforce DML fails afterward, the checkout shows a recovery message asking the customer to email support with the order id — the charge stands, the Salesforce side can be fixed manually.
Supported payment methods
| Method | Status |
|---|---|
| Credit / debit card | Fully wired and tested. |
| ACH (US bank account) | Tokenization is implemented in Apex, and the checkout UI default config has it enabled. Verify with a sandbox before exposing. |
| Mail a check | Display-only — the LWC shows the Payment Mailing Address from your config, but there is no automated reconciliation. |
| Pay later (unpaid Opportunity) | Toggle paylater on the checkout component to create an open Opportunity without charging. Useful for invoicing workflows. |
Recurring donations
If your org has NPSP installed:
- The checkout's
vaultPaymentMethodpath stores the card token and creates annpe03__Recurring_Donation__crecord. - Existing Pledged Opportunities for the same Account are withdrawn first to avoid duplicates.
- Frequency, amount, and payment method are stamped on the recurring donation.
If NPSP is not installed, the recurring path quietly returns "Recurring Donations (npe03) package is not installed; skipping recurring setup." — no error, no record created.
Step 9 — What records show up after a sale
For each completed order:
| Record | Where to find it |
|---|---|
| Contact | Matched or created by the fuzzy lookup. Look on the Contact tab — search by email. |
| Account | If NPSP, the household Account auto-created. Otherwise the Contact's Account (may be null in non-NPSP orgs). |
| Opportunity | Named Liminal Order #<orderId> (or Volunteer – <Event> – <Role> – <email> from the volunteer flow). Amount = total paid. Lim Order ID field carries the cart id. |
| Liminal Selections | One per line item. Status = Ordered, with the price and (for slot offerings) the booked Start/End times. |
Reports:
- Opportunities WHERE Lim Order ID starts with "LIM-" → all paid Liminal orders.
- Liminal Selections WHERE Status = "Ordered" AND CreatedDate = THIS_WEEK → this week's bookings/sales.
- Liminal Offerings WHERE Engagement =
<Campaign>→ inventory dashboard for an engagement.
Step 10 — Inventory math (how rollups work)
The package maintains several rollup fields automatically.
On Liminal Offering:
Quantity Provided= sum of Quantity across all Stock-type Availabilities. Maintained by the Availability trigger.Quantity Claimed= count of Selections in statusClaimed(i.e. in someone's cart).Quantity Ordered= count of Selections in statusOrdered(paid).Quantity Consumed=Claimed + Ordered(formula).Quantity Available=Provided − Consumed(formula).
Slot-style offerings have a parallel set (Slots Provided, Slots Claimed, Slots Ordered, Slots Consumed, Slots Available).
Warning: Carts that get abandoned still hold inventory as Claimed. There is no automatic cart-expiration today. If you see your
Quantity Availablelooking lower than expected, query for staleClaimedSelections and delete them — they will roll off the count automatically.
Step 11 — Customizing what the customer sees
Branding
- Replace the bundled images and fonts in the liminal_static static resource bundle. Most assets are exposed via
${STATIC}/...URLs in the LWCs. - Tweak Lightning Design System theme tokens at the Experience Cloud or community level to recolor buttons, headers, etc.
Customer detail form
The set of fields shown on checkout is driven by Campaign.Lim_Customer_Detail_Template__c. The template is parsed at runtime — you can vary the required fields per engagement (e.g. require shipping address for product orders, only require email for donations).
Confirmation email
There is no confirmation email shipped for the paid checkout flow (the included Volunteer_Confirmation_Email_Autolaunched_Flow is for the volunteer side). To send a receipt, build a record-triggered Flow on Opportunity that fires when StageName changes to your Completed_Order_Opp_StageName__c value, and send an emailSimple action with your own template.
Common questions
Can a single page combine ticket sales and slot booking?
Yes — drop both Liminal: Ticket Storefront and Liminal: Calendar on the same page, give them the same orderId, and Checkout will charge the combined total.
What if Braintree is down during checkout?
The package returns "The payment processor appears to be down. Wait a few minutes, refresh the page, then try again." No partial records are written — the Selections stay Claimed and the cart can be retried.
Can I refund through Liminal? No automated refund flow. Issue the refund directly in Braintree, then either delete the affected Selections or move the Opportunity to a refund stage of your choosing.
Can I use my own payment processor?
The Apex is structured to allow it — Lim_PaymentUtility.checkoutOrder switches on paymentProcessor. Only 'Braintree' is wired today; adding Stripe or another would require a new utility class on the same pattern as Lim_BraintreeUtility.
Why does the calendar show 9 months of slots?
That is the package default (CALENDAR_MAX_DATE = today + 274 days). Currently hardcoded; a future release will make it configurable.
How do I clean up abandoned carts?
Either schedule a batch Apex job in your org to delete Selections in Claimed status older than N days, or build a Flow with a scheduled trigger. There is no built-in mechanism.
Why are the Liminal records showing the toflourish__ prefix?
Liminal is a namespaced managed package — all custom objects and fields carry the toflourish__ prefix when installed in a subscriber org. This is normal Salesforce managed-package behavior; the prefix does not appear on field labels, only in API names.
Quick setup checklist
- Custom metadata record
FL_Liminal__mdtwith nameglobalcreated and populated (Braintree Token, Sandbox Mode, Completed Order StageName, support email). - Permission set Liminal Admin assigned to all admin users.
- Remote Site Settings
FL_BraintreeandFL_BraintreeSandboxactive. - Campaign (engagement) created, marked Active, with Start/End dates and Event Image URL.
- Liminal Offering records created, linked via Engagement, with Label, Description, Image, and Price set.
- Liminal Availability records created with the correct record type (Stock / Slot / Hybrid) and capacity values.
- Lightning page built with
Liminal: Ticket Storefrontand/orLiminal: CalendarplusLiminal: Checkout, all sharing oneorderId. - Guest user permissions and sharing rules in place if publishing publicly.
- Confirmation email Flow built (record-triggered on Opportunity).
- End-to-end smoke test in incognito with a Braintree sandbox card.
- Production cutover: flip Braintree Sandbox Mode off in
FL_Liminal__mdt.
Troubleshooting
| Symptom | First place to look |
|---|---|
| Storefront page is empty | Campaign is not Active, or guest user cannot see it, or the Engagement Id on the component is wrong. |
| Calendar shows no slots | No Open-type Availabilities, or they are outside the Campaign's date range, or every day is blocked by a Close window, or Weekend_Availability = Unavailable is hiding the only days you scheduled. |
| Checkout says "No org-wide settings have been configured" | The global FL_Liminal__mdt record is missing or named differently. |
| Checkout charges but no Opportunity appears | Look for the customer-recovery message — there was a Salesforce DML failure after the charge. Search Opportunities by Lim Order ID = <orderId> and create one manually if needed. |
| Inventory doesn't decrement | The Selection's Price__c is null (volunteer-style records don't count toward Quantity_* rollups). They still count toward Slots_* rollups. |
| Duplicate Contact created | The fuzzy match needs both a name component and an email or phone exact match. Records with only addresses can slip through; tighten data hygiene or pre-create the Contact. |
| Inventory math looks stale | Save any minor edit on the Offering or Availability to retrigger the rollup. |
| Recurring donation didn't get created | Check that NPSP is installed (npe03 namespace) and the running user has access to npe03__Recurring_Donation__c. |
| Payment form won't load on Experience site | Open browser dev console — usually CSP blocking the Braintree script. Add *.braintreegateway.com and *.braintree-api.com to CSP Trusted Sites if needed. |