Every marketer has asked the same question: will this land in Primary or Promotions?
Nobody outside Google can answer that with certainty. Gmail decides the tab using the message content, the sender's history, and how each recipient has engaged with that sender before. Two people on the same list can get the same email in different tabs. Any tool that claims to predict placement is overselling.
When we built the "Gmail tab estimate" in Cresca, we started from that limitation. This post covers what we built, how it works and where it falls short.
What we're actually estimating
The feature does not predict the tab. It estimates how promotional the email looks from its content: the one input to Gmail's decision that the sender fully controls. The UI says this directly. Expand the "Why" panel and the first line reads:
Gmail decides the tab using your content, your sending history and how each person has engaged with you before. This estimates how promotional the email looks.
The output is one of three labels, never a percentage:
- Likely Primary
- Could go either way
- Likely Promotions
We avoided showing a raw score because "73%" suggests a precision we don't have.
Where it runs
The estimate runs entirely in the browser on the rendered HTML, the same markup that will be sent. It's a pure function, so it re-runs whenever the email or subject line changes, inside the AI conversation view, the design editor and the template preview:
const result = useMemo(
() => analyzeInboxPlacement(html, { subject }),
[html, subject],
);
Running it on the client made sense. It's quick, it updates as you edit, and it needs no network call. It also keeps us honest, because it's a small set of readable rules rather than a black box.
Step 1: Ignore what every email shares
We parse the HTML with DOMParser and remove nodes that would add noise:
doc
.querySelectorAll(
'[data-cresca-footer], style, script, title, [style*="display:none"], [style*="display: none"]',
)
.forEach((node) => node.remove());
Every Cresca email gets an automatic compliance footer (unsubscribe link and address), and many have a hidden preheader. Since those are the same on every email, counting them would add noise and hide what the sender can change.
Step 2: Measure the signals
We measure the features that make an email look like a newsletter or campaign rather than a message from a person:
- Images, ignoring very small ones (48px wide or less, such as social icons), since a logo-sized icon isn't the same signal as a hero banner.
-
Distinct links, excluding
mailto:,tel:and in-page anchors. - Buttons: links styled as buttons, counted once each even if several selectors match.
-
Coloured blocks: elements with a
bgcolorthat isn't white or a light grey. We use a simple colour test: a colour is neutral if its RGB channels sit within 24 of each other and all are above 200. - Multi-column layout.
- Sale wording in the subject and body: "% off", "discount", "promo code", "shop now", "limited time", "last chance", "ends tonight" and similar phrases, found with one regex.
- Text volume: a word count, so we can flag an email that is mostly images.
-
Personal markers: a
{{first_name}}token or a greeting like "Hi" or "Hello" at the start.
Step 3: Score it, simply
The score starts at 100. Each signal subtracts a fixed penalty, with heavier penalties for stronger signals:
| Signal | Penalty |
|---|---|
| 3+ images / 1–2 images | 25 / 10 |
| 6+ links / 3–5 links | 20 / 10 |
| 2+ buttons / 1 button | 15 / 8 |
| Coloured banners or blocks | 15 |
| Multi-column layout | 10 |
| 3+ sale phrases / 1–2 | 25 / 10 |
| Under 60 words and at least one image | 10 |
| Personal greeting or first-name token | +5 |
The result is clamped to 0–100. A score of 70 or more is "Likely Primary", 45–69 is "Could go either way", and anything lower is "Likely Promotions".
I should be clear about where these numbers came from. They are hand-tuned weights, not a trained model. We based them on Google's public guidance and on how personal email differs from campaign email: personal email is mostly text, with few links, no banners and one column. The weights express a judgment about which signals matter most. They are not measured from Gmail's behaviour, and we don't present them that way.
Step 4: Explain, don't just score
A label alone isn't useful. Every penalty also records a reason and a fix, and the "Why" panel shows them:
- "4 images: use one image or none, because image-heavy emails look like marketing."
- "Sale wording: % off, shop now: promotional wording is a strong Promotions signal."
- "1 button: a plain text link reads more personal than a button."
When there are no penalties, the panel says the email reads like a personal email: mostly text, few links, no banners.
The panel also gives three tips that don't depend on content: send from a person's name, ask readers to reply (replies show Gmail your emails are wanted), and remember that sales and big launches belong in Promotions, which is normal. That last tip matters. A product launch should look like a promotion. We don't want people removing every image from a launch email to chase the Primary tab.
The one-click rewrite
In the AI conversation view, if the estimate isn't "Likely Primary", there's a Rewrite as a plain personal email action. It sends the current email back to the AI agent with an instruction to turn it into a plain, personal-style message. The estimate then re-runs on the new HTML, so you can compare before and after.
What I'd tell anyone building something similar
- Name what you can't see. Engagement and reputation aren't visible to a content check. Say so in the UI, not in a footnote.
- Prefer labels to percentages when your confidence is coarse.
- Every penalty needs a fix. A score without an action just makes people anxious.
- Don't fight the user's intent. Some emails are meant to be promotional.
- Keep it readable. A rule-based heuristic you can explain beats an opaque model you can't defend when someone asks "why?"
We'll keep tuning the weights as we learn. The design principle stays the same: show people what they control, and be honest about what they don't.
If you want to try it on your own emails, Cresca is free for 30 days at https://www.cresca.xyz, no credit card needed. Build an email with the AI agent or a template and open the "Why" panel before you send.
Lalit Kumar Jangid, founder of Cresca
Top comments (0)