# Show signed-in users a message that fits their plan

> Free users need a reason to upgrade; paying ones need what's new. Tell GeoEngage the plan with one line of code, then aim each message at it.

Use cases · Published 2026-11-01

A visitor on your free plan and a visitor paying for your top plan read the same pages, but they need different things from them. The free user needs a reason to upgrade, shown once and at the right moment. The paying customer needs what's new, and no sales pitch at all. If your site tells GeoEngage which plan someone is on, a rule per plan can show each of them the right message, on your marketing site and inside your app.

## Tell GeoEngage what your site knows

Your site knows who's signed in. GeoEngage doesn't, until your code tells it. There are three ways, all under [Customer data conditions](https://geoengage.app/docs/rules/conditions#customer-data):

| Your site already has | Use the condition | Example |
| --- | --- | --- |
| Nothing it shares yet | **Visitor Attribute**, after one line of code | `plan` is `free` |
| A JavaScript object with the user | **JS Variable (dot path)** | `myApp.user.plan` is `free` |
| A cookie or a stored value | **Cookie Value** or **Local Storage Value** | `plan` is `free` |

The first is the cleanest. Where your app knows who's signed in, call `setAttributes` with what a message needs:

```js
GeoEngage.setAttributes({ plan: 'free', signed_in: true });
```

- **Calls merge.** A later `setAttributes({ plan: 'pro' })` changes `plan` and keeps the rest.
- **Set it early.** An attribute takes effect from the next page view or route change, not on the page that sets it.
- **It stays in the browser,** so a plan set in your app is still known when the same person reads your pricing page on the same domain. A subdomain such as `app.yoursite.com` keeps its own, so set the attribute on both if your app and your site live apart.
- **Clear it on sign-out** with `clearAttributes()`, so the next person on a shared computer isn't greeted as a customer.

See [`setAttributes`](https://geoengage.app/docs/widget/methods#setattributes) for the details.

## What to say to each plan

| Visitor | What helps | Example message |
| --- | --- | --- |
| On the free plan | What the next plan adds, shown once | Ready for more? See what Pro adds |
| On a paid plan | What's new, not a pitch | New in Pro: invite your team |
| On a paid plan, reading your pricing page | Where they stand | You're on Pro. Here's what Business adds |
| Signed in, reading your sign-up page | That they don't need to sign up again | You already have an account: go to your dashboard |

These are examples: write your own, and keep each message to what's true for every visitor it reaches.

## Build it

1. Create a rule. Under **When visitor matches**, add **Visitor Attribute**, with the attribute name `plan` and the value `free`.
2. To show it only inside your app, add **Page URL/Path** starts with `/app`, or whatever your app's pages start with.
3. Choose **Slide-in Panel**, write the message, and point its button at your pricing page. A slide-in panel doesn't stop someone in the middle of their work the way a popup does ([Choosing between similar actions](https://geoengage.app/docs/rules/actions#choosing-between-similar-actions)).
4. Set **Display Frequency** to *Once Every X Days*, so a free user isn't asked on every page ([Display frequency](https://geoengage.app/docs/rules/timing#display-frequency)).
5. Choose **Save & Publish**.

[Try this rule in the GeoEngage rule builder](https://geoengage.app/try), no account needed.

## Keep customers out of offers

The same attribute works the other way. Add **Visitor Attribute** `plan` is not `pro` to a rule with a first-order discount or a free-trial banner, and paying customers stop seeing an offer they can't use. With several paid plans, add one such condition for each.

Visitors who have no plan at all, such as someone on their first visit, still match: a value that isn't there is never `pro`. So the offer keeps reaching the people it's for.

## Check that it helped

Make the upgrade a goal: call `trackConversion('upgrade')` where the upgrade goes through, or count the page that confirms it, and add a goal for it ([Goals and conversions](https://geoengage.app/docs/analytics/goals)). Then turn on **A/B Testing** for the rule, with one version against nothing, so you see whether the message moves upgrades rather than only clicks ([A/B tests](https://geoengage.app/docs/rules/ab-tests)).

## Common questions

### Is a plan name personal data?

A plan describes the account, not the person, so keep attributes to that kind of detail: a plan, a role, a company size. Don't put an email address or a name in an attribute. To tie reports to a signed-in user, `identify()` exists for that, where your privacy policy covers it ([`identify`](https://geoengage.app/docs/widget/methods#identify)).

### Can a visitor change their own attribute?

Yes. It's kept in their browser, so someone who knows how could edit it. Use attributes to choose a message, never to decide what someone may access: that stays with your own sign-in.

### Does it work in a single-page app?

Yes. The widget checks your rules again on each route change, so an attribute set on sign-in applies from the next screen ([Single-page apps](https://geoengage.app/docs/widget/single-page-apps)).

---

Source: https://geoengage.app/blog/message-for-signed-in-users-by-plan
