What you get
A website project runs your site’s content; your site shows it.
MINT is where your team edits the website — its pages, their text and images, their SEO. Your site (Next.js, Astro, a static site, anything that can call an API) reads it with two calls. Make one by choosing Website when creating a project; list the site’s domains on the project for analytics.
The website kit
Three ordinary models, in a “Website” sidebar section. Change them like any model.
| Model (address) | Holds |
|---|---|
Pages (pages) | Every page: its name, path, status, template, parent and place in the menu. |
SEO (seo) | Per page: title, description, share image, keywords, canonical URL, hide from search engines. |
Contents (web-contents) | What each page says, one typed piece at a time — a hero, cards, questions, a list (Contents, page by page). |
They start with a read-only public API (List and Read one, open to anyone), which is what the site API uses. Add fields freely; keep the models’ addresses and that public API, or the site API can’t find them.
The site’s own settings — name, logo, favicon, contact details, default SEO, tags — aren’t a model: they’re on Site setup, one set per website. (Websites made before this had a Site settings table; its record was copied to Site setup, and the table can be deleted in the model builder.)
Building a page
A page, its SEO and its content blocks.
- Under Pages, add a page: name “About”, path
/about(the home page is/), template, and status Draft while you work. - Open it. Its Contents tab lists its blocks — Add there creates one already linked to the page. Add a heading block, a rich-text block, a gallery…
- Its SEO tab: add the title and description search engines and link previews show.
- Set the page to Published. Your site shows it from then on.
Contents, page by page
Pick a page, then add what’s on it. Each piece has a type, and asks only for its own words.
Contents opens with a Page dropdown: choose Home, About… to see that page’s contents in the order they show, or Whole site for what’s on every page — the header and the footer. The arrows move a piece up or down; the pencil opens it.
New content asks first what it is, then shows only that type’s fields. Type in the search box to find a type by name or by what it’s for; above the rest, Suggested offers what usually comes next — an opening for an empty page, the header and footer for the whole site, and after a hero: text, cards, numbers…
| Type | What it holds |
|---|---|
| Hero | A small line above, a heading, text, a picture, two buttons and a few numbers under it |
| Hero carousel | Slides that move on, each with its own heading, subheading, two buttons and background picture — and how many seconds each shows |
| Text / Rich text | A heading and a few paragraphs — or a longer piece with headings, lists and pictures |
| Cards | A heading and a row of cards, each with a title, small text, description, picture and link — features, services, team, plans |
| Questions | Questions and their answers |
| Numbers | A few figures with a label each |
| Reviews | Quotes with a name, role and photo |
| Gallery / Logos / Picture / Video | Photos, logos, one picture with a caption, or a video |
| Call to action | A heading, a line and the buttons |
| List | Records of one of your models — see Lists of your records |
| Section | Anything else: headings, text, a picture, buttons and rows |
| Header / Footer | The words and links at the top and bottom of every page |
The types are components, under Build → Components (category content): add a field to one and every piece of that type gets it; make your own component in the content category and it’s offered as a type too. Each piece is checked as its type. Table opens the same records as a table; pieces from before types (one word each, as the site builder made them) are listed under the page and open there.
Every text field is plain words — a box where each new line is a new line on the site — in every type, your own types included. Only Rich text has the editor with headings, lists and links. Words written in the editor before are shown as plain text the next time the piece is opened.
Drafts until you publish. A change — made here, in the table or in the site builder — is a draft: each piece says Draft, Published or Changes not published. Publish on a piece, or Publish the page, puts it on your site and API; the site builder’s Publish puts the words it shows live with the layout. Publishing needs the Publish permission. The site builder writes each section’s words into one piece of the section’s type, so what you add there shows here, in the page’s order.
More in one piece
A Text with a Gallery under it, a Hero with a Section after it: blocks that belong together stay in one piece.
Under a piece’s own fields, More in this block adds more blocks to it. While it’s empty it suggests what goes well with the piece — under a Text: a Picture, a Gallery, a Video; under a Section: Cards, a Gallery, a Picture — one click adds one. Add opens every type with a search box, the suggested ones first (Enter adds the first match) — any type but List, which is a piece of its own — and shows that type’s fields. Each block moves up and down, folds away, is copied or removed; one piece holds up to 20. The list says +2 blocks beside a piece that has them.
They go live with the piece when it’s published, in the order you put them. The site builder only changes the piece’s own words; the blocks you added here stay.
Slugs: how a site finds a piece
Every piece has a slug — a short name like home-intro. That’s what connects it to your site.
A slug is made for each new piece from its page and type (home-hero-3f9a2c), or you type your own when you add or edit it: lowercase letters, numbers and dashes, one piece per slug. The list shows each piece’s slug with a copy button, and the piece’s form shows the addresses to read it.
| Your site | How it reads the piece |
|---|---|
| Made with the site builder | Each text on the page is tied to a piece’s slug and field — you don’t need the address. Change a slug, and places that use the old one stop showing it until they’re tied again. |
| Your own site or app — one piece | GET /public/api/web-contents/by-slug/home-intro?apiKey=mint_pk_… — its type, its words, and the blocks added under it (block: the piece’s own first, then the others in order, each with its type in __component). |
| Your own site or app — a whole page | GET /public/api/pages/by-path?path=/about&apiKey=mint_pk_… — the page, its SEO, and its pieces in the order they show. Draw each by its type, or pick one out by its slug. |
Lists of your records
Show your projects, posts or products on a page: which ones, how many, in what order.
- Model — whose records it shows. The site reads them as any visitor would, so the model needs its public API on for listing; one without is offered with the reason.
- How many and Order by — newest first, the cheapest first, by name.
- Which records — conditions on its fields (Featured is Yes, Year is more than 2020), all of them or any one. With none, every record its public API shows.
- Card title, text and picture — which of its fields each card shows, and Each card links to:
/projects/{slug}fills in each record’s own values.
It shows now lists what it would show at this moment, or why it shows nothing.
Older content blocks
Websites made before content types kept one word per record. Category says what kind.
| Category | Uses the fields |
|---|---|
| Content | Content (heading or main text), Sub content, Button text and Url |
| Rich content | Rich content — formatted text with headings, lists, links and images |
| List / List of links | List — one entry per item |
| Card | Cards — rows of image, title, subtitle and description |
| Image / Gallery | Image / Gallery |
| Video | Video URL |
| Section / Other | Whatever your site’s design needs |
- Priority orders blocks on the page — higher first. Section and Slug let your site group or find a particular block (“hero”, “faq”).
- Is visible off, or status Draft, hides a block without deleting it.
- Background colour, font colour and font sizes are there for blocks that need their own style; Note and Ref image are for your team only.
SEO
One SEO record per page: Title (up to 120 characters) and Description (up to 320) for search results, a Share image for link previews, keywords and tags, a Canonical URL if the page lives at another address too, and Hide from search engines. Your site puts them in the page’s head (see the example below); pages without one fall back to the site settings’ defaults.
A page’s SEO page
Open a row on Website → SEO to edit all of that page’s SEO on one page, with its score and previews beside it.
The fields are in five parts: Search result, Sharing, Search engines, Structured data and Sitemap. The buttons at the top jump to each part. Beside them, the score and the Google and shared-link previews change as you type.
Changes are kept on the page until you press Save (or ⌘S / Ctrl+S). While there are any, a bar at the bottom offers Discard and Save, and leaving the page asks first.
Across the site lists the site SEO health problems this page is part of, like another page with the same title, with links to those pages. It reflects the last save. Page opens the page’s own record, and View on site opens it on your domain. The Link preview tab checks the live page the way Facebook, LinkedIn, WhatsApp, X and Slack read it (Link previews) — save and publish a change before checking it; on a page drawn per record, type one record’s address. Overview, Comments and History are in the tabs, as on any record. To link the SEO to a different page, use Edit in the row’s ⋯ menu on the table.
The SEO score
Beside each page’s SEO, and under Basics in the edit form: a score from 0 to 100 and what to fix, worked out as you type.
Type the page’s Focus keyword, the one phrase people would search to find it, and the checks look for it. The score is Good from 80, Needs work from 50 and Poor below that, and it shows under the Google preview too. Problems come first, each with what to do.
| Check | Good when |
|---|---|
| Visible to search engines | Hide from search engines is off. A hidden page can’t rank at all. |
| Title and description | Title 30–60 characters, description 70–160. Longer ones get cut off in results. |
| Keyword in the title | It’s there, near the start. |
| Keyword in the description | It’s there. Google shows the searched words in bold. |
| Keyword in the address | It’s in the page’s path, like /wireless-headphones. Not checked on the home page. |
| Keyword in the main heading | The page’s first heading has it. |
| Keyword in the opening words | It’s in the first 100 words. |
| How often | About 0.5–3% of the words. More reads as stuffing. |
| One page per keyword | No other page has the same focus keyword. Two pages after one phrase both rank lower. |
| Page length | 300 words or more. |
| One main heading | A page built elsewhere has exactly one h1. |
| Short sentences | Under 20 words on average (English pages). |
| Share image | The page has its own. Using the site’s image counts half. |
The page’s words come from the site builder for pages built there (the saved draft), and from the live page for a site built elsewhere. When they can’t be read, the checks that need them are left out rather than failed, and the score says why. The focus keyword is never published.
Structured data
Under Structured data on a page’s SEO: say what the page is and fill in a short form, for star ratings, prices, opening hours and FAQs in search results.
Pick the Schema type and its form appears. The page’s title, description, address and share image are added for you, so the form only asks for the rest. Under it, Google needs lists what the type can’t do without, and Would help what makes the result richer.
| Type | The form asks for |
|---|---|
| Organization | Name and logo (the site’s when left empty), phone, email, address, social profiles. |
| Local Business | The same, plus the kind of business, price range, a map link and opening hours. Google needs the address. |
| Product | Brand, SKU, image, price and currency, availability, rating and number of reviews. Google needs a price with its currency, or a rating with its count. |
| Article, Blog Posting | Author and their page, published and updated dates, image. The site is the publisher. |
| FAQ Page | Questions and their answers. Only pairs with both are used; they should be on the page too. |
| Event | Start and end, in person or online or both, status, venue and address or the online link, ticket price and link, performer, organiser. |
| Service | The service, the area served, who provides it, and a starting price. |
On a page made one per record, like /shoes/[slug], each text field has a { } button: pick one of the record’s fields and every shoe’s page fills it in, for example the price from {{record.price}}.
Advanced: your own JSON-LD still takes any JSON object; it goes over the form, so what you write there wins. After publishing, the link under the form opens Google’s Rich Results Test for the page (for a page per record, test one record’s address). On the home page, Site setup → SEO’s structured data is used when the page’s type is Auto.
Site SEO health
At the top of Website → SEO: problems that only show when your published pages are looked at together, and the gaps on each.
Each problem lists its pages; click one to open its SEO page. Errors come first, then warnings, then notices. Check again (the arrows) runs it once more after you’ve fixed something.
| Problem | What to do |
|---|---|
| The whole site is hidden from search engines (error) | Site setup → SEO → Indexing is off. Turn it on when the site is ready. |
| Canonical URL points to a page that isn’t on the site (error) | Search engines follow it and drop the page. Point it at a published page, or clear it. |
| Hidden from search engines, but in the menu | Allow indexing, or take the page out of the menu. |
| Pages with the same title / description | Give each page its own, so search engines can tell them apart. |
| Pages with the same focus keyword | Two pages after one phrase both rank lower. Give each its own phrase. |
| No title of their own / No description | Write them in the page’s SEO. Without a title, results show the page’s name. |
| Titles over 60 / descriptions over 160 characters (notice) | The end gets cut off in search results. Put what matters first. |
| No share image | Add one to each page, or a default in Site setup → SEO (then it’s only a notice). |
| Hidden from search engines (notice) | Check these are meant to be hidden. |
| Published pages with no SEO here (notice) | For a site built elsewhere, these keep their own SEO. Add them on Website → SEO to set it here. |
Only published pages count. Pages made one per record (like /blog/[slug]) aren’t compared with other pages, since their titles are filled in per record.
The site API
Three read-only calls, with the project key (API → Keys).
| Call | Answers |
|---|---|
GET /site | { settings, menu } — the site settings, and the menu: [{ _id, name, path, parent }]. |
GET /pages/by-path?path=/about | { page, seo, contents } — the published page at that path, its SEO (or null), and its visible, published blocks in order. 404 if there’s no published page there. |
GET /web-contents/by-slug/home-intro | One published piece by its slug: its type and block — its own words first, then any blocks added under it. 404 if there’s none. |
const site = await fetch('https://mint-c5e1255519fc.herokuapp.com/public/api/site?apiKey=mint_pk_…').then(r => r.json());
const about = await fetch('https://mint-c5e1255519fc.herokuapp.com/public/api/pages/by-path?apiKey=mint_pk_…&path=/about').then(r => r.json());
const intro = await fetch('https://mint-c5e1255519fc.herokuapp.com/public/api/web-contents/by-slug/home-intro?apiKey=mint_pk_…').then(r => r.json());Need something else — every block with section “faq”, say? The models’ own public API is there too: GET /web-contents?section=faq.
Rendering your site
One route that renders whatever path is asked for.
An example with Next.js — fetch the page by its path, put its SEO in the head, and draw each block by its category. Any framework works the same way.
// app/[[...slug]]/page.tsx — Next.js, one route for every page
const API = 'https://mint-c5e1255519fc.herokuapp.com/public/api';
const KEY = 'mint_pk_…'; // the project key — API → Keys
const load = async (slug: string[] = []) => {
const path = '/' + slug.join('/');
const res = await fetch(`${API}/pages/by-path?apiKey=${KEY}&path=${encodeURIComponent(path)}`, { next: { revalidate: 60 } });
return res.ok ? res.json() : null;
};
export async function generateMetadata({ params }) {
const data = await load((await params).slug);
return data?.seo
? { title: data.seo.title, description: data.seo.description,
openGraph: { images: data.seo.image ? [data.seo.image] : [] },
robots: data.seo.noIndex ? { index: false } : undefined }
: {};
}
export default async function Page({ params }) {
const data = await load((await params).slug);
if (!data) return notFound();
return (
<main>
<h1>{data.page.name}</h1>
{data.contents.map(block => <Block key={block._id} block={block} />)}
</main>
);
}function Block({ block }) {
switch (block.category) {
case 'rich-content': return <section dangerouslySetInnerHTML={{ __html: block.richContent }} />;
case 'image': return <img src={block.image} alt={block.name} />;
case 'gallery': return <div className="gallery">{block.gallery.map(src => <img key={src} src={src} alt="" />)}</div>;
case 'list': return <ul>{block.list.map(item => <li key={item}>{item}</li>)}</ul>;
case 'card': return <div className="cards">{block.card.map((c, i) => <article key={i}><h3>{c.title}</h3><p>{c.description}</p></article>)}</div>;
case 'video': return <video src={block.videoUrl} controls />;
default: return <section><h2>{block.content}</h2><p>{block.subContent}</p>
{block.btnText && <a href={block.url}>{block.btnText}</a>}</section>;
}
}Images
Image fields hold the file’s public address from your project’s Media, ready for an img tag.
The website’s home
Open a website project and its dashboard is about the site.
- Traffic — page views, visitors, visits and bounce rate for the last 30 days, against the 30 before, and views per day. The full reports are on Analytics.
- Set up — what’s still missing: name, logo, favicon, default SEO, a published page, SEO on every page, a domain, tracking. Each has a link to where it’s set; the list goes once everything is done.
- Pages — each page’s path, status, whether it has SEO, and how many content blocks it has.
Numbers, charts and lists you add in the dashboard builder show below.
Site setup
Site → Site setup: everything a website needs besides its pages, on one page — not a table.
Its tabs are General, Contact & social, SEO, Tracking, Server-side, Code, Redirects & headers, Domains and Check the site. Each setting is a card with its own Save, enabled once you change something. Your site reads it all from the site API, so a change is live within a minute with no code change. Changing it needs the Build permission; domains need Manage projects.
Name, logo and favicon
General has the site’s name, tagline, logo, favicon and footer text, and its theme: primary and secondary colour and font. Images come from your Media library. The analytics script adds the favicon to pages that don’t have one.
Contact and social links
Contact & social: email, phone, WhatsApp, opening hours, address and a map embed, and the full addresses of your Facebook, Instagram, X, LinkedIn, YouTube, TikTok and Pinterest pages. Your site shows the ones you fill in.
Default SEO
On the SEO tab, Default SEO is the same SEO editor as each page’s: a Google preview on your site’s domain, then Basics, Social (with a share preview), Indexing, Structured data and Sitemap, with counters and SEO checks. Above it, the title template — %s · Acme makes a page called “About us” show as “About us · Acme”.
What it covers depends on the kind of field. The meta title, description, share image and keywords are the home page’s, and the fallback for any page without its own. Settings — the Open Graph type, the X card and image, indexing switches, max snippet and image preview, sitemap priority and change frequency — apply to every page that doesn’t set its own. Share titles and descriptions, the schema type, custom JSON-LD and the canonical URL are the home page’s only, so pages don’t all share one.
Each page’s own SEO is under Website → SEO, or a page’s ⋯ → Name, address & SEO in the site builder: A page’s SEO.
Tracking tags and pixels
Paste an ID, save its card, and the tag is on every page — the analytics script adds it.
| Tag | The ID looks like |
|---|---|
| Google Analytics 4 | G-XXXXXXXXXX |
| Google Tag Manager | GTM-XXXXXXX |
| Google Ads | AW-123456789 |
| Meta Pixel | A long number |
| TikTok Pixel | Capital letters and digits |
| LinkedIn Insight | The partner ID, a number |
| Pinterest Tag | A long number |
| X (Twitter) Pixel | o1a2b |
| Snap Pixel | A 36-character ID with dashes |
| Microsoft Clarity | The project ID |
| Hotjar | The site ID, a number |
This works on any site with the analytics script on its pages. Page views on sites that change pages without reloading are counted by the pixels too. MINT analytics turns the panel’s own visit counting on or off. A site that renders its tags itself adds data-no-tags to the script.
Server-side tracking
Browsers block a good share of tracking; this server can send the events too.
| Provider | What the server sends — and what it needs |
|---|---|
| Meta Conversions API | Page views, Lead (a record your site sends in, e.g. a contact form) and CompleteRegistration (a customer signs up). Needs the Meta Pixel ID and an access token from the pixel’s Conversions API settings. |
| Google Analytics (Measurement Protocol) | generate_lead and sign_up. Needs the Google Analytics ID and an API secret from your web data stream. |
Turn each on in Site setup → Server-side. Keys are kept on the server: once saved, the panel only shows that one is set, with Replace and Remove. Meta gets each page view from the pixel and the server with the same event id, so it counts it once; Google can’t do that, so page views go to Google from the browser only. Visitors are matched by IP address, browser and the platform’s cookie; an email or phone in a form is hashed before it’s sent.
So a lead is matched to the visitor who sent it, the site adds window.MintAnalytics?.headers() to the headers of its own calls to the site API. Use Meta’s test event code while you check it in Events Manager → Test events, then clear it.
Add script
Paste any script a provider gives you and it’s on every page of your website.
- Open Site setup → Add script and press Add script.
- Give it a name (so you know it later), choose where it goes — in
<head>(tags that must load first, verification metas), at the start of<body>(a<noscript>fallback) or at its end (chat widgets) — and paste the script exactly as given, with its own<script>or<meta>tags. - Add. It’s saved at once and live within a minute. Switch it off to pause it without losing it; Edit or Remove it any time.
Good for Google Tag Manager, Microsoft Clarity, Hotjar, a chat widget (Tawk.to, Crisp, Intercom), a Facebook domain-verification tag — anything a provider gives you as HTML. On a site made in the site builder the scripts are written into each page’s HTML, so they run on the first load and verification checkers find them; a site with its own code gets them from the analytics script. Only add scripts you trust: they run with full access to your pages and visitors.
Indexing, robots.txt and sitemap
- Let search engines index the site — off while you build; robots.txt then asks them all to stay away.
- Sitemap — a sitemap.xml named in robots.txt: your published pages, and every record a template page shows (each blog post at
/blog/my-post, each product) with the date it last changed and its image. Anything marked Hide from search engines is left out: a page, a whole template page, or one record with its own switch. - Extra sitemap addresses — pages Mint doesn’t know about, one per line:
/old-page, or a full address on your site. Addresses on other domains are ignored. - Main domain — the address used in the sitemap and robots.txt.
- Google Search Console and Bing — the verification codes, added as meta tags.
Your site serves them from the site API: /site/robots.txt and /site/sitemap.xml — the tab shows the full addresses. A site built with AI wires them up for you.
Past 5,000 addresses the sitemap becomes an index of numbered parts, /sitemap.xml?part=1, ?part=2 and so on, as search engines expect. Sites made in the site builder and sites behind the MINT Worker pass the part on for you; a site you serve yourself should pass ?part= on to the site API too.
Your robots.txt and AI crawlers
Site setup → SEO → robots.txt: what search engines and AI crawlers may read. Built for you, or written by you in full.
Built for you starts from the Indexing settings: everything allowed (or everything blocked while indexing is off), your Extra rules, and the Sitemap line. Each crawler has a switch; turn one off and the file tells it to stay away.
| Crawler | What it’s for |
|---|---|
| Googlebot, Bingbot | Search results. Blocking one takes your site out of that search engine. |
| Google-Extended | Training Gemini. Doesn’t affect Google Search. |
| GPTBot | Training OpenAI’s models. |
| ChatGPT-User | ChatGPT opening a page when someone asks it to. |
| ClaudeBot | Training Anthropic’s models. |
| PerplexityBot | Perplexity’s answer engine. |
| CCBot | Common Crawl, an open web archive many AI models are trained on. |
| Bytespider | Training ByteDance’s models (TikTok’s owner). |
| Applebot-Extended | Training Apple’s models. Doesn’t affect Siri or Spotlight. |
Write the whole file starts from the file built for you, and the site then serves exactly what you write: the switches, Extra rules and Indexing don’t change it. With indexing off, every page still says noindex.
Under the editor you see the file and a check for the usual mistakes: the whole site blocked, Googlebot or Bingbot blocked, no Sitemap line, a Sitemap line without a full address, Noindex lines (search engines ignore them), and lines crawlers don’t understand. Crawlers are asked, not forced: the well-known ones listen, others may not.
Redirects and headers
Redirects send an old address to a new page or another site — 301 permanent when a page moved for good, 302 temporary otherwise. Response headers are sent with your pages, for security (X-Frame-Options, Content-Security-Policy) or caching. Both come to your site in GET /site under config; its code applies them (a site built with AI does).
Domains
Where your site is live, e.g. example.com and www.example.com. Analytics only counts visits from these (and from localhost while you build), so others can’t send visits in your name. With none, visits from anywhere count.
The quickest way is on Analytics, under the project’s name: once the snippet is on your site, the site your visits come from shows there with Add beside it, one click. Add domain (or Domains) opens the list: every site visits came from in the last 30 days, a box to type one in (a whole address like https://www.example.com/about is fine), and the domains you have, each with a × to remove. Adding a domain needs the Manage projects permission.
Check the site
Is every tag really on the live site?
Check the site opens your live home page (the main domain, or the first domain) and shows each tag set here as On the site, Check this or Not on the site. It also finds tags the page adds by itself: the same ID twice means every visit is counted twice; another ID means the page has its own. Google Tag Manager confirms your container exists, and Meta confirms the Conversions API token is valid and made for your pixel. Each Tracking card shows its result from the last check.
Link previews
Site setup → Link previews: what Facebook, LinkedIn, WhatsApp, X and Slack show when someone shares a page.
Those apps never run a page’s JavaScript. They read the HTML your server sends and build the card from its title, description and share image tags. So a page can look right in the browser and still share badly. Type any address — yours or anyone’s — and press Check: you see each app’s card the way it lays it out (big picture, small thumbnail or text only), the share image’s size, and what to fix. Each page’s SEO has the same checker in its Link preview tab, already filled with that page’s address.
- Tags only added in the browser come first. On a connected site, the snippet’s
data-seoputs the SEO set here on the page in the browser — Google sees it, the share apps don’t. Print it on your server, or put the Mint edge in front of the site (Connect a website). A page that’s a JavaScript app with an empty<div id="root">is flagged the same way. - The share image: 1200×630 (1.91:1), a JPEG, PNG or WebP, under 300 KB for WhatsApp. Under 600×315 it shows as a small thumbnail; under 200×200 not at all; over 5 MB X and LinkedIn refuse it, over 8 MB Facebook. Its address must be a whole one (
https://…). - Title and description: about 60 characters for the title, under 160 for the description. Add
twitter:cardsummary_large_imagefor X’s big card, andog:site_nameso Slack and Facebook show your site’s name. - Edit SEO opens the page’s SEO when the address is one of this project’s pages; Add SEO when the page has none yet.
Connect a website you already have
Site → Website setup → Existing website: one line on your site, then pick which pages take their SEO from here.
A site that’s already built — WordPress, Webflow, Wix, Squarespace, Shopify, or one made by an agency — connects without rebuilding it. You get this project’s Analytics on it, and you edit the SEO of the pages you choose here instead of in the site.
- Paste the snippet into the
<head>of every page, as high as you can. Website setup shows it with your project key filled in, and where it goes on each platform (a header-code box or your theme’s header). - Add the pages Mint should handle: on Website → Pages, a page with its address as it shows in the browser (
/about), set to Published; then its SEO on Website → SEO. - Test the connection: open your site once in a browser, then type a page’s address in Website setup and press Test connection.
What Test connection checks
| Check | What it tells you |
|---|---|
| Domain | The address is one of your domains (Site setup → Domains). Visits from other addresses aren’t counted. |
| Snippet | The page’s HTML has the snippet with this project’s key and data-seo. Added by a tag manager or a plugin that loads it later? It may not be in the HTML; Visits then shows whether it runs. |
| Visits | Page views are arriving, with the last page and when. This shows the snippet runs in visitors’ browsers. |
| SEO | Whether the page takes its SEO from here or keeps its own, and whether the HTML your server sends already has it (then link previews see it too). |
Connected means nothing is missing and the snippet is on the page or visits are arriving. For the raw answer a page gets, open /site/head with its path (below): "found": false means the page isn’t added.
Which SEO a page gets
- A page you added to SEO: what you fill in here replaces the page’s own title, description, share image and the rest. A field you leave empty keeps the page’s own, and Default SEO only fills in tags the page doesn’t have.
- Any other page: keeps its own SEO. The snippet leaves it alone, even with Default SEO set.
/about,/about/and/about.htmlare the same page. Changes reach the site within a minute, without a deploy.
Link previews and other crawlers
Google runs the snippet, so it reads the SEO. WhatsApp, Facebook, LinkedIn and X previews, and most other crawlers, only read the HTML your server sends. If you can edit your site’s server, Website setup has short code for PHP, Node / Express and Next.js that prints the same tags into the page there. It prints nothing for pages you haven’t added, so your own tags stay. Keep the snippet in the page too: it counts the visits.
GET https://mint-c5e1255519fc.herokuapp.com/public/api/site/head?apiKey=mint_pk_…&path=/about
GET https://mint-c5e1255519fc.herokuapp.com/public/api/site/head?apiKey=mint_pk_…&path=/about&format=htmlWebsite setup: connect your code
Site → Website setup: how a site you build yourself reads this project — step by step, for your framework.
Pick what your site is built with — Plain HTML, React (Vite) or Next.js — and the page shows the code for each step, already filled in with your project’s API address, project key and domain. Copy it into your site:
- Keys — where they go: a small
site.jsfor plain HTML (there’s no build step),.envfor Vite,.env.localfor Next.js — and the same names on your host. - Connect — a few lines that call the site API: the site and its menu, a page by its path, a piece by its slug, a list of records.
- Show a page — one route that draws whatever page is asked for.
- Analytics — the analytics script in every page’s head (Next.js: the root layout). It counts route changes in React apps by itself, and brings in every tag from Tracking and Add script.
- SEO — each page’s title, description and share image from Website → SEO, and
robots.txtandsitemap.xmlpassed on from this project (a rewrite on Netlify or Vercel, two route handlers in Next.js).
Keys, and where each setting goes
Two values go in your code; everything else is set here and reaches the site without a deploy.
| Setting | Where it goes |
|---|---|
| Site API address | Your settings: MINT_API (server), VITE_MINT_API (Vite), NEXT_PUBLIC_MINT_API (Next.js client code). |
Project key (mint_pk_…) | MINT_API_KEY, VITE_MINT_API_KEY or NEXT_PUBLIC_MINT_API_KEY. It’s publishable: it reads only what’s published and what your endpoints allow, so it can be in a page. |
Server key (mint_sk_…) | Only your server’s settings, as MINT_SERVER_KEY — never with VITE_ or NEXT_PUBLIC_, never in a page. Make one on API → Keys, only if your server calls endpoints that need it. |
| Google Analytics, Tag Manager, pixels | Site setup → Tracking |
| Chat, verification and other scripts | Site setup → Add script |
| Default title, description, share image | Site setup → SEO |
| Each page’s SEO | Website → SEO |
| Your domain | Site setup → Domains |
After the first deploy, add the site’s domain (analytics counts visits only from your domains) and run Check the site. On Vercel or Netlify, environment variables go in the project’s settings; Vite writes them into the build, so redeploy after changing one.
Build your site with AI
Build the site with Claude Code (or any AI editor) and it’s managed from here the moment it’s deployed.
Connect your AI to this website project, then ask it to build your site — “a two-page site for my bakery: a home page and an about page, and a menu of our products”. While it writes the code, it puts everything the site shows into this project instead of into the code:
- Site setup — name, logo, favicon, colours, font, contact details, social links, default SEO, tags, and the domains it’s deployed on.
- Pages — each with its SEO and its content blocks in order: headings, text, buttons, lists, cards, images.
- Images — uploaded to your Media library.
- Lists — products, services, team, testimonials — become models of their own, linked where they belong, with their public API on.
- Analytics — the tracker on every page.
The site’s code reads all of it from the site API. Once deployed, change a heading in Contents, a price in Products or the favicon in Site setup, and the live site shows it within a minute — no code change, no redeploy.
Building again
Asking the AI to rebuild or change the site updates the same pages, blocks and records rather than adding copies: pages are matched by path, blocks by their slug on the page, list records by a field such as their slug. A block the AI drops is archived — hidden from the site, still in the panel. Note that a rebuild writes the AI’s text over edits you made in the panel to the same blocks.
Blogs, products and more
A website project is a full project: build a Posts or Products model, make it public (List and Read one, open to anyone), and your site lists them through the public API. Add the sign-in widget for members’ areas, and the tracker to count visits.
Starter code from a template
A website made from a template can come with the code its site starts from.
When the template has starter code, the website’s home shows a Starter code card: a link to the repository, a Deploy it button for its host, and the settings (environment variables) the code needs — your project’s API address and public name already filled in. Copy the repository (or press Deploy it), paste the settings where the host asks for them, and the site shows your pages and content straight away. Change the code as you like; it reads everything through the site API.
Troubleshooting
| Symptom | Why, and what to do |
|---|---|
| “No published page at that path” | The page is still a draft, or its path differs — /about, not about. |
| A block doesn’t show | It’s on another page, Draft, or Is visible is off. |
| /site returns 404 | The project is an app — the site API is for website projects. |
| A tag isn’t on the site | Run Site setup → Check the site: it says whether the analytics script is on the page and which tags it found. |
| The menu is missing a page | It isn’t published, or In the menu is off. |
| Changes take a minute to appear | Your site caches the answers (revalidate: 60 above). That’s on your side. |