Render testPlaywright, iPhone emulation: bytes, requests, LCP, CLS — and JS-off HTML
Link checkAll 104 llms.txt URLs hit for status codes
PlatformsG2, Google Ads Transparency, Meta & LinkedIn ad libraries, PH, Reddit, YT, X
AI engines3 buyer prompts run on Perplexity, permalinks retained
The five findings
filter ↓
F-01
Every hub page links to nothing
Critical
# extract every internal anchor from raw HTML
$ curl -s https://www.usefini.com/blog \
| grep -oE 'href="(/[^"]*|https://www\.usefini\.com[^"]*)"' \
| sort -u
https://www.usefini.com/blog← self-link
https://docs.usefini.com/introduction
https://security.usefini.com/
# same result on every collection page:
/blog 127 posts → 1 link
/hall-of-fame 35 profiles → 1 link
/podcast 11 episodes → 1 link
/resource-library 5 reports → 1 link
/comparison → 1 link
/podcast-episodes → 0 links
Child slugs exist in the HTML as Framer CMS payload strings — never as <a href>. Verified 4 Aug 2026.
Raw-HTML anchor extraction — no JS execution, i.e. exactly what a non-rendering crawler receives
Basis
Anchor text and internal links are primary relevance signals. GPTBot, ClaudeBot, PerplexityBot and CCBot largely do not execute JavaScript
So what
To those crawlers our blog, podcast, hall of fame and research library are unreachable by link. 1,555 pages, no link equity flowing between them
What the two crawler types actually receive
Exact fix — 6 steps, ~half a day
Open the Framer project → /blog page → select the CMS Collection List component that renders the post cards.Framer editor → Pages → blog → Layer panel
Select the card frame inside the list. In the right panel open Link → set it to the collection field Slug (Framer writes a real <a href> only when the card itself carries the link, not when an on-click override does).Right panel → Link → Collection Reference → Slug
Confirm the card title is a text layer inside that linked frame, so the slug text becomes the anchor text. Do not link the image alone.
Publish, then re-run the check on each page — the count must be the number of children, not 1.curl -s https://www.usefini.com/blog | grep -c 'href="/blog/'
In Search Console → URL Inspection → "Test live URL" → View crawled page on /blog. The raw HTML tab must now list the post URLs.
Done whencurl -s https://www.usefini.com/blog | grep -c 'href="/blog/' returns ≥ 127, and the same test passes on all nine collection pages.
F-02
We publish three different prices
Critical
# 1. homepage visible copy
$ curl -s https://www.usefini.com/ | strip-tags | grep -o '\$0\.[0-9]*'
$0.49 per resolution
# 2. homepage JSON-LD, same page, same request
$ curl -s https://www.usefini.com/ | jq '..|.offers?'
"description": "... 2,000 resolved tickets per month.
$3,600/mo ... Additional resolutions $0.89 each."
# 3. /pricing meta description
$ curl -s https://www.usefini.com/pricing | grep 'name="description"'
"See Fini pricing: pay per resolution at $0.69..."
# 4. our own G2 listing
Growth $0.69 1 Resolution
# bonus: languages, same page, two schema blocks
Organization → "50 languages"
SoftwareApplication → "130+ languages"
Four sources. Three prices. All published by us. Verified 4 Aug 2026.
Fetched each surface, extracted visible copy, JSON-LD offers and meta description separately
Basis
Retrieval-based models resolve conflicting facts by hedging or omitting. A hedged price loses a comparison to a competitor who published one number
Related
Same problem on entity data: LinkedIn says San Francisco / 51–200, YC says Amsterdam / 14, schema HQ is Wilmington DE, schema names 1 of 2 founders
Exact fix — 8 steps, ~2 hours
Get the real number from Deepak in writing: list price per resolution, the Growth plan monthly, the overage rate, and whether $0.49 is a volume tier or an outdated figure. Nothing else starts until this exists.
Framer → homepage → update the pricing text layer to the canonical number.Also check the pricing comparison module and any CTA microcopy
Framer → Site Settings → Custom Code → End of <head> → edit the SoftwareApplication JSON-LD: fix offers.price, offers.description and the language count.
In the same block fix Organization: language count to match, add Hakim as a second founder, and extend sameAs with the G2, YC, Crunchbase and YouTube URLs.
Update /pricing meta description + on-page copy to the canonical number.Framer → pricing → Page Settings → SEO
Update the price on every off-site profile: G2 (Sell on G2 → Product Profile → Pricing), HubSpot, Front, Gorgias, Zendesk and Intercom marketplace listings, Crunchbase, LinkedIn.
Regenerate llms.txt with a ## Facts block containing the canonical set, so models get one unambiguous source.
Done whenA search for the price string across usefini.com, the schema, llms.txt and all off-site profiles returns exactly one value. Re-audit quarterly.
F-03
Our headline claim reads as 0% to AI crawlers
Critical
# what a non-rendering crawler receives from the homepage
$ curl -s https://www.usefini.com/ \
| python3 -c "strip script/style, print text" \
| grep -o '0%.\{0,60\}'
0% Resolution Rate0% Accuracy Rate0+ Supported Languages0M+ Monthly RESOLUTIONS# the real values only exist after JS runs the count-uphumans + Googlebot → 90% / 99% / 130+ / 3M+GPTBot, ClaudeBot, PerplexityBot, CCBot → 0
Confirmed by fetching the homepage and stripping <script> before reading text. Verified 4 Aug 2026.
Where
usefini.com/ hero stat band, above the fold
Method
Raw HTML fetch, scripts stripped, text extracted — replicating a non-rendering crawler
Basis
The counters initialise at 0 and animate to the real figure in JS. Anything that doesn't run JS captures the initial value
Nuance
This is not "the site is broken" — humans and Google see it correctly. It is specifically a GEO defect, on the one claim the whole positioning rests on
Exact fix — 4 steps, ~30 minutes
Framer → homepage → select each of the four stat text layers in the hero band.
Change the default text content from 0 to the true value (90%, 99%, 130+, 3M+) so the correct number is what ships in the HTML.The count-up override then animates from a rendered-correct starting state
If the animation component forces a 0 start, either set its start value to the true figure minus a small delta, or drop the animation. A static correct number beats an animated wrong one.
Republish, then verify with the same command used to find it.curl -s https://www.usefini.com/ | grep -o '0% Resolution Rate' → must return nothing
Source:g2.com/products/fini/reviews · captured 4 Aug 2026 · Note the Sponsored — Aissist.io box: a 38-review competitor is buying placement on our profile page.
Intercom Fin3,898
Zoho Desk7,820
Agentforce1,202
Aissist.io38
Fini13
G2 review volume, same category. Bars drawn to true scale against Zoho Desk (7,820). Fini’s 13 is 0.17% of that bar — the hairline is the honest picture. G2 Score = satisfaction × market presence, and market presence is driven by review count — so 4.9/5 cannot rank on 13 reviews.
Opened the category and read all of page 1. Fini appears nowhere on it.
Also checked
Capterra, Trustpilot, TrustRadius, Gartner Peer Insights — no Fini profile found on any
Basis
G2 category pages are among the most-retrieved sources in AI answers. This is why reviews, not more content, is the real GEO lever
Maths
100+ customers × 25% response = 25+ reviews. The rating is already the best in the category
Source: G2 category page 1 of 37 · captured 4 Aug 2026 · Fini is not present on this page.
Exact fix — 90-day review sprint, 9 steps
Pull the customer list and score each account: live > 60 days, resolution rate at or above target, no open escalation. That is the eligible pool.
Rank by a proven-value moment — a resolution-rate milestone, a QBR win, a successful peak season. Ask at the moment, never on a quarterly blast.
Get the G2 review-collection link: Sell on G2 → Reviews → Review Generation → create a campaign link. Use a unique link per CSM so attribution is clean.
Write one three-line ask. Name the specific result they got, one link, state the time cost ("about four minutes"). No template merge fields beyond the first name.
Assign it: each CSM owns a named list with a monthly number. Put it in the CS scorecard, not in a marketing tracker — the request has to come from the person the customer trusts.
Offer the incentive G2 permits (gift card via G2's own program), and disclose it. Never draft the review.
At ~25 reviews, submit to Capterra + GetApp — one Gartner Digital Markets submission covers both.
At ~50, open Gartner Peer Insights — enterprise fintech buyers read it during procurement and it is heavily cited by AI engines.
Defend the profile: buy the G2 category placement that Aissist.io is currently using against us — only after the review count justifies the click.
Done when50 G2 reviews inside 90 days (≈ 4/week), Fini visible in the category grid, and G2 URLs appearing as citations in the monthly prompt panel.
F-05
Every comparison page claims we win every row
Strategic
Won. "best AI customer support agent for a fintech in regulated industries" → Lorikeet #1, Fini #2. Our only citation was usefini.com itself. perplexity.ai/search/cb497e22…
Lost. "best alternatives to Intercom Fin" → Quickchat, Helply, Pluno, Gorgias, Sierra/Decagon/Ada. Fini absent — beaten by companies far smaller than us. perplexity.ai/search/1281b323…
Method
Ran three real buyer prompts on Perplexity, retained the permalinks, recorded which vendors were named and which sources were cited
Finding
Scanned all five of our comparison surfaces for concession language — "when to choose them", "limitations", "better if". Zero instances. On every page.
Basis
Models discount sources that read as vendor marketing and quote ones that read as evaluations. Lorikeet beats us on our own vertical with a fraction of our content
Our comparison content is scattered across four incompatible places
Nothing links to anything. The two best pages are buried in the blog.
Exact fix — 10 steps, 6 pages in 30 days
Pick the six competitors by where we actually lose: Intercom Fin, Zendesk AI, Decagon, Sierra, Ada, Gorgias.
Build one Framer template with five fixed blocks: verdict paragraph → comparison table → "choose them if" → FAQ → CTA. Everything else is content entry.
Write the verdict block first, ≤100 words, stating plainly who should pick Fini and who shouldn't. This is the block AI engines extract verbatim, so it carries the whole page.
Build the table as a real HTML <table>, not Framer bar graphics. Rows: regulated fit · audit trail · price per resolution · guarantee · days to go live · channels · certifications./comparison currently has 0 tables — graphics are invisible to extraction
Write an honest "Choose <competitor> if…" section. Example: "Choose Fin if you're already deep in Intercom's helpdesk and want the fastest possible setup." This is what converts the page from an ad into a citable evaluation.
Add FAQPage JSON-LD with the five questions buyers actually ask (price, migration, compliance, accuracy, go-live).Framer → Page Settings → Custom Code → End of <head>
Migrate the two strong blog pages to /compare/ and 301 the old URLs.Framer → Site Settings → Redirects
Fix the Zendesk page: retitle to the query we want to win, and put Fini in the H1.
Refresh or retire /blog/fini-ai-vs-ada — it is stamped "Last Updated: Feb 17, 2025" and quotes stale competitor pricing.
Merge /comparison into /compare, 301 the loser, and link the hub to every child (this is F-01 step 4).
Done whenSix pages live under /compare with tables and concession sections, all /blog/fini-vs-* 301'd, and Fini named on the "Intercom Fin alternatives" prompt at the next monthly panel run.
The AI engines, tested directly
3 prompts · permalinks retained
Prompt run on Perplexity
Fini
Who won, and from which sources
Proof
best AI customer support agent for a fintech in regulated industries
#2
Lorikeet #1, Fini #2, Zowie #3 — our citation was usefini.com itself, theirs was independent
The pattern: we win the narrow vertical prompt and lose both high-volume ones. The winners on those two are tiny companies whose own blog posts got cited — they wrote the head page, we wrote 1,221 long-tail guides.
Where we stand against the field
all figures measured, not estimated
Paid search presence
Google Ads Transparency Center, all regions, all time · 4 Aug 2026
Decagon~300
Lorikeet66
Fini0
Lorikeet — our closest-stage competitor — is running a "Decagon Alternative" ad right now. That is the exact motion recommended for us, already validated in our category. adstransparency.google.com · lorikeetcx.ai
Followers / subscribers / upvotes. LinkedIn is the only real distribution asset — and its profile says San Francisco, 51–200 employees, industry "Chatbot Software".
X — 170 followers, 34 posts since Oct 2022
Recommendation: maintain as an entity signal, do not invest
youtube.com/@finiai · captured 4 Aug 2026 · The buried asset is the podcast: 11 interviews with Heads of Support, no transcripts, no schema.
Page weight — Framer is not the problem
Measured with Playwright, iPhone emulation, cold load · total transferred bytes
Lorikeet14.4 MB
Fini home13.4 MB
Decagon11.3 MB
Fini pricing5.2 MB
Fini guide4.0 MB
Verdict: keep Framer. Our TTFB is 267 ms against Decagon's 780 ms and Lorikeet's 417 ms, and our content is server-rendered. The one genuine issue is 8.85 MB of JavaScript on the homepage — cap it with a budget, don't migrate the site.
The order I'd do it in
nothing in week 1 needs budget
Week 1Free · ~1 day total
F-02
Publish one price everywhereGet the number from Deepak, then copy → schema → llms.txt → G2 → marketplaces
F-03
Server-render the hero statsFour text layers. Stops crawlers reading our resolution rate as 0%
F-01
Turn on real links in every CMS collection listNine pages. Makes 1,555 pages reachable without JS
—
Fix the LinkedIn profileLocation, headcount, and the "Chatbot Software" industry tag
—
Repair llms.txt2 dead links, 7 stale redirects, add the canonical facts block
F-04
Open the G2 review sprintCS-owned, 4 reviews a week, target 50
Days 1–30Build
F-05
Six comparison pages on one template, each with a "choose them if" section
—
Internal-linking template + lastmod in the sitemapOne template change upgrades 1,220 existing pages
—
Rebuild the ROI calculator — it's a 404 and it's listed in our llms.txt
—
Transcribe the 11 podcast episodes≈40,000 words of original, attributable content we already own
—
Attribution before the campaigns, not after
Days 30–90Spend
—
Google Ads: brand defence + competitor terms, landing on the new compare pages
—
Founder-led LinkedIn cadence, every post landing on-domain
—
PR: Microsoft partnership, the bank logos, EU AI Act timing, and the listicles AI engines cite
—
One named bank case study with real numbersICICI or PostFinance — the asset no competitor can match
—
Re-run the prompt panel and report share-of-voice against today's baseline
Two limits, stated up front. PageSpeed Insights hit its daily API quota twice, so the load figures come from my own Playwright measurement rather than a Lighthouse score. And without Ahrefs or Semrush I inferred backlink authority from verifiable listings rather than an index. Everything else here was observed directly and every command is reproducible.
Full row-by-row comments, the page-by-page deep dive and all 35 tests with methods are in the workbook — Fini GTM — investigation, evidence & fix plan.xlsx