EBA Application Funnel

How the Everything But Asana™ Certification sales funnel runs on an Adhara Program — what the setup script configures in Adhara, what ships with a deploy, and what Adhara does on its own.

The Everything But Asana™ (EBA) Certification is sold through an application funnel:

Applicant submits form → CRM contact created → tag EBA Certification Applicant → answers stored → fit decided → qualified applicant gets a booking page → call booked → tag EBA Application Call Booked → reminder → call happens → salesperson marks Accepted / Nurture / Not a Fit → accepted applicant gets an enrollment (checkout) link → enrolled.

Almost none of that logic lives in this repo. It runs inside Adhara Programs, Adhara’s built-in “application funnel” feature. This site’s job is small: show the pages, send the form to Adhara, and report visits so the funnel numbers are complete.

The confusing part is that three separate things are involved, and they change in different ways. This page separates them.

The three layers

Layer What it is Where it lives How it changes
1. Adhara configuration The Program itself: its form, tags, decision rules, emails, booking link, product, price Adhara’s database, in the edm-ecosystem workspace npm run eba -- setup (run by hand, from a laptop), or by editing the Program in the Adhara dashboard
2. Site code The EBA pages, the submit proxy, the visit tracker, hiding the product from /shop This repo Commit and deploy, like any other code change
3. Adhara runtime Everything that happens to an applicant: tagging, deciding, emailing, matching bookings, checkout, enrollment Adhara’s servers Nothing to deploy. It follows whatever layer 1 says

Three rules follow from this:

  • Deploying the site never changes the Program. A deploy only ships layer 2.
  • Running the setup script never changes the site. It only writes to Adhara, and it takes effect immediately, for real applicants. There’s no staging copy.
  • Most day-to-day changes are layer 1. Email wording, rules, tags and price are edited in Adhara, not in code.

The funnel, step by step

Who does what at each step. “Site” means code in this repo. “Adhara” means automatic, driven by the Program’s configuration. “Team” means a person working in the Adhara dashboard.

# Step Who Details
1 Visitor lands on /everything-but-asana or /eba-application Site EbaFunnelTracker reports one “view” per browser session to Adhara. That count is the funnel’s “visitors” number. It also remembers the first-touch UTM codes and referrer.
2 Applicant starts the form at /eba-application/form Site The first answer typed reports a “start” to Adhara.
3 Applicant submits Site → Adhara The browser posts to /api/forms/submit. That proxies to Adhara’s public form endpoint (eba-application) with the answers plus utm_params, referrer and source_url.
4 Contact created Adhara Found or created by email/phone. Name, email and phone are mapped onto the contact.
5 Tag EBA Certification Applicant Adhara The Program’s “received” stage tag.
6 Answers stored Adhara Kept on the application (Program → Applications → open one). They’re not copied into the contact’s custom fields.
7 Team notified Adhara application_received_team email to the workspace admins and owner.
8 Fit decided Adhara Decision rules run straight away. See Decision rules.
9a Not ready → Nurture Adhara The application is marked Nurture (tag EBA Nurture), and the applicant gets the eba-application-not-ready email.
9b Everyone else → Approved Adhara Marked Approved (tag EBA Application Approved). The applicant gets the eba-application-approved email. Its button links to /eba-application/book.
10 Applicant sees the next page Site /api/forms/submit mirrors the same rule to choose the redirect: /eba-application/received (not ready) or /eba-application/submitted (approved, with a booking calendar).
11 Call booked Applicant → Adhara Booked on the eba-application-call scheduling link, from /eba-application/submitted or /eba-application/book. Adhara matches the booking to the application by the booker’s email. The application moves to Call Booked (tag EBA Application Call Booked), and the team gets the call_booked_team email.
12 Reminder Adhara Adhara’s built-in booking reminder, 24 hours before the call.
13 Call happens Team Over Google Meet, as configured on the scheduling link.
14 Outcome recorded Team In the Program’s review screen: Accepted, Nurture or Not a Fit (tags EBA Accepted / EBA Nurture / EBA Not a Fit). If they didn’t show up, use No-show: it puts them back to Approved so they can rebook.
15 Accepted → enrollment link Adhara Because the Program’s on_accept is send_checkout, the applicant gets the checkout_link email. It links to a personal acceptance page hosted by Adhara, where they pay the $5,000 price through Stripe. The link is valid for 30 days.
16 Enrolled Adhara After payment the applicant becomes an active participant, moves to the Enrolled stage, gets the EBA Enrolled tag and the access_granted email. Any access links on the Program are applied (portal tier, course, and so on). None are configured yet; see Open items.

Two details people trip over:

  • The site doesn’t decide anything. The redirect in step 10 mirrors the rule. If you change the rule in Adhara, change EBA_NOT_READY_ANSWER / ebaRedirect() in src/lib/ebaFlags.ts too, or people will land on the wrong page (the emails will still be right).
  • The site sends no emails for this funnel. Every email comes from the Program. Before the Program existed, /api/forms/submit sent them itself. That code is gone.

Layer 1: what npm run eba -- setup configures

scripts/gravity-eba.ts. It’s safe to run again: every step checks what already exists and only fills in what’s missing or out of date. A second run in a row changes nothing (verified). One catch: setup opens the Program, so if you closed it on purpose, running setup reopens it. Run it from a laptop with .env filled in. It doesn’t run during a build or deploy.

Setting Value Notes
Program Everything But Asana™ Certification, entry mode apply Found by name; created if missing. Creating it also creates its pipeline and stages.
Form eba-application, attached with public submissions allowed The same form the site already used. Its field ids (full_name, investment, …) are a contract with form.astro.
Form auto-response Off The form’s own “your application is in” email would arrive next to the program’s approved / not-ready email. Setup sends the form’s settings back with only this flag changed (Adhara replaces settings as a whole).
Stage tags received → EBA Certification Applicant
approved → EBA Application Approved
call_booked → EBA Application Call Booked
accepted → EBA Accepted
nurture → EBA Nurture
not_a_fit → EBA Not a Fit
enrolled → EBA Enrolled
Created in the CRM if missing. Each is applied when the application reaches that stage.
Enrolled stage Enrolled, right after Accepted Adhara treats this stage as optional, so a new Program’s pipeline doesn’t have one. Setup adds it to the pipeline and gives it the enrolled role. Once an accepted applicant pays, Adhara moves their application there, so the board gets an Enrolled column. Needs the leads:write scope. Skipped once the role is set.
Next step call, on scheduling link eba-application-call Or EDM_EBA_APPLICATION_CALL_SLUG.
On accept send_checkout Emails the acceptance/checkout link.
Product & price EDM_EBA_PRODUCT_ID, EDM_EBA_PRICE_ID Setup publishes the product if it isn’t published yet. Adhara only charges for published products.
Decision rules 1. Not ready to invest → Nurture
2. Everyone else → Approve
See below. Kept in this order.
Approval email application_approved → email template eba-application-approved Replaces Adhara’s generic approval email with EDM’s branded one.
Status Opened Only if Adhara reports no opening_blockers; otherwise the script prints them.

Decision rules

Rules run once, when an application arrives. The first rule that matches decides.

  1. Not ready to invest → Nurture: answer investment equals Not at this time → record outcome Nurture and send the email template eba-application-not-ready.
  2. Everyone else → Approve: no conditions (a catch-all) → Approve, which sends the approval email.

Both are auto mode, so they act without a person. To have a person confirm each decision instead, switch a rule to suggest in Adhara. The application then waits with a suggestion on it.

What setup does not touch

These were made by hand in Adhara, and setup only uses them:

  • the form eba-application (its questions and fields)
  • the scheduling link eba-application-call (availability, duration, Google Meet)
  • the email templates eba-application-approved and eba-application-not-ready (wording)
  • the product Everything But Asana™ Life Coaching Certification (eba-certification) and its $5,000 price

Edit those directly in Adhara. Their changes are live immediately and need no script or deploy.

Layer 2: what ships with a deploy

File Role
src/pages/everything-but-asana.astro Sales page; includes the visit tracker
src/pages/eba-application/index.astro Application intro page; includes the visit tracker
src/pages/eba-application/form.astro The 3-step form. Sends answers plus attribution to /api/forms/submit; tracker reports “start”
src/pages/eba-application/submitted.astro Approved: booking calendar, prefilled from the form
src/pages/eba-application/received.astro Not ready: thank-you page, no booking
src/pages/eba-application/book.astro Booking page the approval email links to. Asks for the same email they applied with
src/pages/api/forms/submit.ts Proxies to Adhara’s public form endpoint, passes attribution through, returns the redirect
src/lib/ebaFlags.ts Form slug, the “not ready” answer, the product slug, ebaRedirect()
src/components/EbaFunnelTracker.astro Form view/start beacons, sent from the browser straight to Adhara (they’re rate-limited per IP, so proxying them would make every visitor share one limit), plus the UTM memory
src/services/shopStore.ts PROGRAM_ONLY_SLUGS hides eba-certification from /shop, so it can only be bought through the acceptance link
scripts/gravity-eba.ts The setup/status script. It’s in the repo, but it never runs on the server

The deployed site needs only the usual ADHARA_WORKSPACE (and ADHARA_API_URL if not the default). It doesn’t need ADHARA_PROGRAMS_API_KEY, EDM_EBA_PRODUCT_ID or EDM_EBA_PRICE_ID; only the script reads those.

Layer 3: what the team does in Adhara

Day to day, everything happens in the Adhara dashboard → Programs → Everything But Asana™ Certification:

  • Applications: each one shows the answers, its stage, the rule’s decision, history and emails sent.
  • After a call: record the outcome (Accepted, Nurture, Not a Fit), or No-show. Recording the outcome is also what counts the call as “held” in the numbers, so record every call.
  • Override a decision: approve or decline by hand, or reassign the application to a teammate.
  • Emails: switch any program email on or off, or point it at a different template. The outcome_accepted, outcome_nurture and outcome_not_a_fit emails are off by default. Accepted applicants still get the checkout_link email.

Funnel numbers

npm run eba -- status            # last 30 days
npm run eba -- status --days 90

This prints the Program’s state, anything blocking it, and Adhara’s scorecard: visitors → applications → approved → calls booked → calls held → joined, plus nurture and no-shows. The same numbers, with targets and trend lines, are in the Adhara dashboard under Programs. Monthly targets and a daily emailed report can be set there too.

Where each number comes from:

Number Source
Visitors Form views reported by EbaFunnelTracker, one per browser session across the sales page, intro page and form
Applications Form submissions that became applications
Approved Applications the rule (or a person) approved
Calls booked Approved applications whose booking was matched
Calls held Calls with an outcome recorded (Accepted / Nurture / Not a Fit)
Joined Accepted applicants who paid

Go-live order

The order matters, because once the form is attached to the Program, Adhara owns it:

  1. Deploy the site first. This stops the old site-side emails and hides the product from /shop.
  2. Then run npm run eba -- setup. This attaches the form, publishes the product and opens the Program.
  3. Check with npm run eba -- status. No blockers should be listed.

Why the order matters:

  • Run setup before deploying: the old site code still sends its own emails, so applicants get two of each. The product would also show in /shop.
  • Leave a long gap after deploying: the new site sends no emails, so until setup runs nobody gets one.
  • Close the Program while the form is still attached: submissions are refused with HTTP 410, and the form shows Adhara’s “closed” message.

Changing things later

I want to… Do this Deploy? Script?
Change email wording Edit the template in Adhara (Email → Templates) No No
Change the price Add a new price to the product in Adhara, put its id in EDM_EBA_PRICE_ID, run setup. Stripe prices can’t be edited in place No Yes
Rename a tag Edit TAGS in scripts/gravity-eba.ts, run setup (or change it on the Program in Adhara) No Yes
Change who’s auto-approved Edit the rules on the Program in Adhara. If the “not ready” answer changes, also update src/lib/ebaFlags.ts and deploy Only for the redirect No
Have a person review every application Switch the “Everyone else → Approve” rule to suggest in Adhara No No
Change a question on the form Edit the form in Adhara and STEPS in form.astro (field ids must match), then deploy Yes No
Change call length or availability Edit the eba-application-call link in Adhara No No
Pause applications Close the Program in Adhara (the form then refuses submissions with 410) No No

What a re-run of setup overwrites, and what it leaves alone:

  • Overwritten every run: the stage tags, booking link, on_accept, product, price, approval email and the form auto-response being off. If you change one of these in the Adhara dashboard, change it in the script too, or the next run puts the script’s value back.
  • Added once: the Enrolled stage. Setup skips it when the Program already has an enrolled stage, so renaming or moving that stage in Adhara is kept.
  • Created only if missing (by name): the two rules. Edits you make to a rule’s conditions or action in Adhara are kept. Setup only puts the two rules back at the top of the list. A rule you rename in Adhara gets re-created alongside it under the old name.

Environment variables

Variable Read by Purpose
ADHARA_WORKSPACE Site + script Workspace slug for the public form, booking and analytics endpoints
ADHARA_WORKSPACE_ID Script Workspace id for the authenticated Programs API
ADHARA_PROGRAMS_API_KEY Script only Needs scopes programs:read, programs:review, programs:manage and commerce:write. programs:manage isn’t in Adhara’s preset roles, so it needs a custom role. Keep it out of the deploy environment
EDM_EBA_PRODUCT_ID, EDM_EBA_PRICE_ID Script only The certification product and price the acceptance link charges for
EDM_EBA_APPLICATION_CALL_SLUG Site + script Scheduling link for the Application Call (default eba-application-call)

Open items

  • Keep automatic decisions on. With them off, applicants can still book a call before anyone has reviewed them. See EBA Known Gap: Manual Review.
  • Onboarding isn’t connected yet. Paying makes someone an active participant, but the Program has no access links, so it grants nothing in the portal. Add the portal tier and/or course on the Program (Access) once the student onboarding content exists.
  • Bookings match by email. If an approved applicant books with a different email than they applied with, the call isn’t attached to their application. /eba-application/book asks them to use the same address.
  • Only one reminder. Adhara’s built-in reminder is 24 hours before the call; there’s no “1 hour before” option. Extra follow-ups (for example nudging approved-but-not-booked applicants) can be added as Program Flows, which need Adhara’s Plus add-on.
  • Visitors are counted per browser session, not per person. A visitor who comes back the next day counts again. Site-wide page analytics are a separate Adhara system and aren’t part of the scorecard.
  • The product is hidden from this site’s /shop only. Once published, it’s visible in Adhara’s own public catalog API and any Adhara-hosted storefront.
  • Payment plans aren’t supported. The acceptance link charges one price, $5,000 in full. Payment-plan applicants are handled by the team by hand for now.

Troubleshooting

Symptom Likely cause
Form shows “applications are closed” / HTTP 410 The Program isn’t open. Run npm run eba -- status, then reopen it in Adhara
Applicant got two emails at once The form’s own auto-response was switched back on. Run setup (it turns it off)
Applicant got no email Check the application’s Emails tab in Adhara. A rule that couldn’t act shows a needs_person decision explaining why (for example no email provider connected)
Booking didn’t move the application to Call Booked They booked with a different email, or the application wasn’t at Approved. Record the outcome manually
Accepted applicant got no checkout link npm run eba -- status prints checkout blockers (product unpublished, price not synced, payment account)
Visitors is 0 The form must be public and the tracker must load. Check the browser console for blocked requests to api.adharaweb.com/api/v1/public/forms/<id>/view
Setup fails with missing required scope ADHARA_PROGRAMS_API_KEY lacks a scope listed above

Adhara’s own reference for everything above is docs/api/programs.md in the adhara_web repo.