# Capture leads and subscribers (/docs/capture-leads) Mark records a lead or a newsletter signup through two public endpoints: `POST /leads` and `POST /subscribe`. Your site keeps its own form, design, and copy — these endpoints are the contract your form's submit handler posts to. Both endpoints live on the API host (for example `https://api.mark.lifewithdata.org`) and take a JSON body over HTTPS. Life With Data gives you the `key` value below; it identifies your workspace and is safe to include in client-side code. ## Before you integrate [#before-you-integrate] * **Your public form key.** Every request body includes `key`, the value Life With Data gave you for your workspace. * **Your site's domain on the allowlist.** Requests from a browser must come from an origin Life With Data has added to your workspace. An origin that isn't allowed gets refused. * **A consent checkbox.** Both endpoints require an explicit consent acknowledgment before they record anything. ## POST /leads [#post-leads] Call this when a visitor submits your lead form. ``` POST /leads Content-Type: application/json ``` | Field | Type | Required | Notes | | ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------ | | `key` | string | Yes | Your public form key. | | `email` | string | Yes | The visitor's email. | | `consent` | `true` | Yes | Must be exactly `true`. | | `consentVersion` | string | Yes | The version of the consent text you showed. | | `pageUrl` | string | Yes | The page the form was on, including any `utm_*` query parameters. | | `renderedAt` | number | Yes | Unix time in milliseconds when the form first rendered. Used to reject a submit that happens too fast to be human. | | `name` | string | No | | | `phone` | string | No | | | `message` | string | No | The visitor's message. | | `personaId` | string | No | An id from your Mark workspace, if your form asks which persona the visitor matches. | | `answers` | object | No | Any other named field on your form: a flat object of strings, numbers, or booleans, up to 40 keys. | | `referrer` | string | No | `document.referrer` at submit time. | | `distinctId` | string | No | Your analytics visitor id, if you have one. | | `fbclid`, `gclid` | string | No | Ad click ids from the page URL. | | `honeypot` | string | No | Leave any hidden honeypot input empty; a filled value marks the submission as a bot. | A successful submission returns: ```json { "leadId": "..." } ``` Show your own thank-you message — Mark doesn't redirect anywhere. If the same email submits again while their lead is still new, Mark attaches the new message to that same lead instead of creating a second one. ## POST /subscribe [#post-subscribe] Call this for a newsletter or marketing-only signup, where you don't want a sales lead created. It takes the same fields as `POST /leads` except `phone`, `message`, `personaId`, and `answers`, and returns: ```json { "contactId": "..." } ``` ## Responses [#responses] | Status | Meaning | | ------------------------ | ---------------------------------------------------------------------------------------------------------- | | `201` | Recorded. The body has `leadId` or `contactId`. | | `200` with an empty body | The submission looked automated (a filled honeypot or an impossibly fast submit) and nothing was recorded. | | `400` | The body is missing a required field, `consent` isn't `true`, or `answers` doesn't fit the shape above. | | `403` | The request's origin isn't on your workspace's allowlist. | | `404` | The `key` doesn't match a workspace. | | `429` | Too many requests from the same address; try again shortly. | ## Example [#example] ```bash curl -X POST https://api.mark.lifewithdata.org/leads \ -H "Content-Type: application/json" \ -d '{ "key": "your-public-form-key", "email": "visitor@example.com", "message": "Interested in a quote", "consent": true, "consentVersion": "1.0", "pageUrl": "https://your-site.com/contact?utm_source=instagram", "renderedAt": 1735000000000 }' ``` # Check site readiness (/docs/check-site-readiness) The **Site readiness** card lists the agent-parseability issues on one website: the things that make it harder for AI answer engines to read and use the site. The card names the site's domain, which opens the site in a new tab, and shows **Scanned** with the date of the last scan. ## Find the card [#find-the-card] * **Home** shows the card for your primary website only, the one with the **Primary** badge in **Settings** > **Websites**. Select **See more** on the card to open that site's page. * **Analysis** > **Website** opens your primary site's page. Select the site's name at the top to switch to another site. There the same issues sit inside the [site audit](/docs/site-audit) report, under the **AI readiness** category, scored with the rest of the audit. If you haven't added a website yet, **Home** shows "No website yet. Add one to see how ready it is for AI answer engines." Select **Add one** to go to **Websites**. ## Run the first scan [#run-the-first-scan] A site with no report yet shows "No site readiness report yet for", then the site's domain. Select **Run scan** to check it. ## Read the issues [#read-the-issues] If the scan finds issues, the card shows a summary such as "3 issues found", with a count for each tier that has issues: * **essential**: an agent can't work around it. Fix these first. * **recommended**: lower stakes than essential. * **bonus**: lowest stakes. Select the summary to show or hide the list. It starts hidden. Each issue in the list shows its name, its tier as a badge, what the scan found, and what to change. Essential issues come first. If the scan finds nothing, the card says "No agent-parseability issues found." ## Scan again [#scan-again] After you change the site, check it again: 1. Go to **Home**. 2. Select **Scan again**. The button reads **Scanning…** while it works. On a site's page, select **Check now** on the **AI readiness** tile of the site audit report instead. When it finishes, the card says "Updated just now." and shows the new results. If the scan can't run, the card says "Could not check right now. Try again in a few minutes." and keeps the last results on screen. # Connect social accounts (/docs/connect-social-accounts) Connect your Instagram or X account on the **Channels** page so Mark can schedule and publish posts there once they're approved. Open it from the sidebar: **Content** > **Channels**. The page has four groups: **Social**, **Website**, **Email**, and **Ads**. Each channel gets a card with its connection status and its publishing rules. ## Connect Instagram or X [#connect-instagram-or-x] 1. In **Social**, find the **Instagram** or **X** card and select **Connect**. 2. Sign in to the platform and authorize Mark. You leave Mark for this step. 3. The platform sends you back to **Channels**, which shows "Instagram is connected." or "X is connected." The connected card shows the account handle, like `@verdella`. If authorization doesn't finish, **Channels** shows an error instead and the card stays **Not connected**. Select **Connect** to try again. To switch accounts or repair a connection, select **Reconnect** on a connected card. It runs the same authorization again. ## What each card's status means [#what-each-cards-status-means] * **A handle, like `@verdella`.** The account is connected and Mark can publish to it after approval. * **Not connected**, with a **Connect** button. Select **Connect** to connect the account. * **Connecting is not set up in this environment.** This copy of Mark can't connect social accounts. Ask Life With Data. * **Coming soon** badge. This channel isn't self-serve yet. Only Instagram and X connect from **Channels** today. ## Set publishing rules [#set-publishing-rules] Open **Publishing rules** on a channel card to set how posts on that channel get approved and how often they go out. Select **Save** after a change. Each channel has its own rules. * **Active**: whether Mark writes for this channel. * **Approval policy**: **Required** sends every post to a reviewer before it can be scheduled. **Auto** approves a post the moment it's ready, as long as it has no claim flags, so no reviewer sees it first. * **Approver role**: which role should approve posts on this channel, **Member** or **Approver**. It's a note for your team and for Mark. It doesn't limit who can approve. * **Cadence count** and **Cadence period**: how many posts you want per week or per month. **Calendar** shows progress against it, like "2 of 3 this week". Leave the period on **No cadence** to skip it. * **ICP fit**: whether this channel reaches your ideal customer, **Confirmed** or **Uncertain**. * **Notes**: anything Mark should keep in mind for this channel. Social cards also have a **Bio destination**, the page your profile link points to. Once it's set, the card shows a **Profile short URL** to paste into the profile. For why review comes first and who approves, see [Review and approval](/docs/concepts/review-and-approval). ## Website rules [#website-rules] The **Website** card covers the blog posts, FAQs, landing pages, and competitor pages Mark writes for your sites. Open **Rules by content type** to set the same rules for each content type separately. Select **Manage websites** to go to **Websites**, where you add and manage the sites themselves. ## Other connections [#other-connections] Channels only connects social accounts. Google, Drive, and other services connect under **Settings** > **Connections**. # Create and submit a post (/docs/create-and-submit-a-post) Write a post in the post editor, then submit it for review. For what each status means, see [Review and approval](/docs/concepts/review-and-approval). Open your posts from the sidebar: **Content** > **Posts**. The list shows every post in your workspace, newest change first, with its **Channel**, **Status**, and **Last updated** time. If there are none yet, it says "No posts yet. Start one with New post." ## Start a draft [#start-a-draft] 1. Select **New post**. 2. Pick a **Channel**. 3. Select **Create draft**. The post opens in the editor. To switch channels later, change **Channel** in the editor. Fields that the new channel doesn't use are cleared. ## Write the post [#write-the-post] The editor shows the post's status at the top, next to **Channel**. * **Body**: write the post. The counter shows its length against the channel's limit, like 180 / 2200 on Instagram. Over the limit, the counter turns red and the editor says "The body is over the 2200 character limit for instagram." **Save draft** and **Submit for review** can't be selected until you shorten it. * **Angle**: pick an angle for the post, or leave it on **No angle**. Angles come from **Strategy** > **Angles**. For setting them up, see **Create and assign angles**. * **Scheduled time**: pick the date and time the post should go out, in your timezone. Leave it empty if you don't know yet. * **Media**: select **Attach media**. In the dialog, drag files in to upload them, or select files this workspace already uploaded. Select **Done**. To take a file off the post, select the remove button (**×**) in the corner of its thumbnail. If uploading isn't available in your workspace, the dialog says so. Files that are already uploaded are still there to pick. * **Details**: fields for the channel you picked, so they change when the channel does. Fields marked with **\*** are required. List fields read "One value per line". Put each value on its own line. The preview beside the editor updates as you type. Instagram and X show the post as it will look on the channel. Other channels say there's no preview. ## Save or submit [#save-or-submit] * Select **Save draft** to keep your work. **Saved** appears next to the buttons. The post stays a draft and stays editable. * Select **Submit for review** when it's ready. Mark saves the post first, then sends it to review. The status changes to in review. If the channel's **Approval policy** is **Auto**, Mark can approve it right away instead. See [Connect social accounts](/docs/connect-social-accounts). If something needs fixing, the editor says so above the buttons. "Some fields need attention." means a **Details** field has a problem. The message shows under that field. Once you submit, the post is locked. Trying to save it shows "This post is locked for review. It can be edited again after a reviewer requests changes." To see what happens next, read [Review and approve posts](/docs/review-and-approve-posts). ## Edit a post later [#edit-a-post-later] Select a post in the **Posts** list to open it in the editor. Drafts and posts with changes requested stay editable. Other posts are locked. After a reviewer requests changes, fix the post and select **Submit for review** again. # Read the Home Google cards (/docs/home-google-cards) Home shows two Google cards side by side. **Organic search** comes from Google Search Console. **Website activity** comes from Google Analytics (GA4). Each card covers the last 28 settled days and compares them with the 28 days before. The cards appear only when the workspace has a website. With no website, Home says **No website yet. Add one** instead. ## Read the numbers [#read-the-numbers] **Organic search** shows: * **Clicks**: how many times someone clicked through to your site from Google Search. * **Impressions**: how many times your site appeared in Google Search results. * **Average position**: where your site ranked on average. Lower is better. * **Top searches**: the searches that brought the most clicks. **Website activity** shows: * **Sessions**: visits to your site. * **Users**: people who visited. * **Conversions**: key events counted in Analytics. This figure shows only once the property has key events to count. * **Top landing pages**: the pages where the most sessions started. Under each figure is its change against the previous 28 days. Counts show a percent change. **Average position** shows what it was before. Green is good news and red is bad news, so a lower average position shows green. The change is blank when there is no earlier figure to compare against, or the earlier count was zero. The card header shows the date range. Hover the info icon next to it to see the range it's compared with and which Google product the numbers come from. ## Why the newest days are missing [#why-the-newest-days-are-missing] Google finishes counting a day a few days after it ends. Search Console data arrives about 3 days late and Analytics data about 2 days late. The cards leave those days out, so a day Google hasn't finished counting doesn't look like a drop. Mark also keeps the numbers for up to an hour, so a card can be up to an hour behind Google. ## When a card shows no numbers [#when-a-card-shows-no-numbers] You manage the Google connection in **Settings** > **Connections** > **Google**. Most fixes happen there. If Google isn't connected, the card says **Not connected yet.** Select **Connect Google**. One sign-in covers Analytics and Search Console. Otherwise the card shows one sentence instead of numbers: | The card says | What to do | | ----------------------------------------------------------- | --------------------------------------------------------------------------------- | | Choose the Search Console property Mark should read. | On the Google connection, choose the Search Console property and select **Save**. | | Choose the Google Analytics property Mark should read. | On the Google connection, choose the Analytics property and select **Save**. | | Connect Google to see numbers here. | Connect Google from the Google connection. | | Reconnect Google and allow this access to see numbers here. | Reconnect Google and allow the access it asks for. | | Google access was revoked or expired. Reconnect Google. | Reconnect Google. | | Google did not answer. Reload to try again. | Reload the page. | When the Google connection needs something from you, the card shows its status and a **Next:** line with the step to take. Select **View connection** to open it. If Home can't read your connections, it says "Search Console and Analytics status could not load. Reload to try again." Reload the page. ## See more detail [#see-more-detail] On a connected **Organic search** card, select **More details** to open **Performance** at the **Search** section. **Website activity** has no detail link. # Overview (/docs) Mark is Life With Data's internal platform for running GTM and ops/automation work for clients. Anthony and Hannah operate it; it is not a public, self-serve product yet. The near-term shape is a product-backed service: Life With Data runs Mark and delivers the work — content, social, demand capture, and eventually ads. Clients get a hosted workspace with a database, scheduled workflows, and a human in the loop, not a pile of disconnected automations. ## Where to go next [#where-to-go-next] * [Concepts](/docs/concepts) — the mental model: tenants, drafts, and the review loop. * [Install the site tag](/docs/install-the-site-tag) — the one script that wires a client's existing site to Mark for attribution. * [Connect social accounts](/docs/connect-social-accounts) — connect Instagram or X from Channels and set each channel's publishing rules. * [Check site readiness](/docs/check-site-readiness) — see what stops AI answer engines from reading a website, and scan it again. * [Site audit](/docs/site-audit) — crawl up to 50 pages of a site, read its health score by category, and work through its issues. * [Manage assets](/docs/manage-assets) — upload images and video, find any file in your workspace, and rename or delete uploads. * [Read Performance](/docs/read-performance) — Google Search clicks and queries, and how each pillar's social posts did. * [Track AI assistant traffic](/docs/track-ai-assistant-traffic) — install the tracking file on a Next.js site and see which AI assistants fetch your pages. * [Capture leads and subscribers](/docs/capture-leads) — the API contract your site's form posts to. * [MCP server](/docs/mcp) — query these docs from Claude, ChatGPT, or another MCP client. More documentation lands here as it is written. # Inspect a URL (/docs/inspect-a-url) **Inspect a URL** asks Google Search Console about one page and shows whether Google has indexed it, and why or why not. It shows Google's last recorded view of the page. It doesn't run a fresh test. ## Find the section [#find-the-section] 1. In the sidebar, select **Analysis** > **Website**. It opens your primary site. To switch sites, select the site's name at the top of the page. 2. **Inspect a URL** is the first section, above **Site audit**. **Performance** also links here. When Google is connected, select "inspect a URL on" and your primary site's name at the bottom of **Search**. See [Read Performance](/docs/read-performance). ## Before you start [#before-you-start] * Google must be connected with Search Console access. * A Search Console property must be chosen on the Google connection. When both are in place, the section header shows "Search Console property:", then the property. Every inspection uses that one property, whichever site's page you're on. The URL you enter must be a page in that property. ## Inspect a page [#inspect-a-page] 1. Enter the full page URL, including https\://. 2. Select **Inspect**. The button reads **Inspecting…** while it runs. The result appears below the form, under the URL you checked. ## Read the result [#read-the-result] Three values sum it up: * **Verdict**: **Indexed**, **Not indexed**, or **Unknown**. * **Coverage**: Google's own wording for the page's index status. * **Last crawl**: when Google last crawled the page, in your workspace timezone. **Never** means Google hasn't crawled it. The details below show why: * **Crawl allowed**: **Yes**, or "No: blocked by robots.txt". * **Page fetch**: whether Google could load the page. For example, **Successful** or "Failed: not found (404)". * **Indexing allowed**: **Yes**, or a reason such as "No: noindex in robots meta tag". * **User-declared canonical**: the canonical URL the page names, or **None**. * **Google-selected canonical**: the URL Google chose as the canonical. * **Sitemaps**: sitemaps where Google found the page. * **Referring pages**: pages Google saw linking to it. A "—" means Google returned no value for that field. **Sitemaps** and **Referring pages** list at most 5 URLs. If Google knows of more, the list ends with "Showing 5 of", then the total. If a value starts with "No:" or "Failed:", fix that on your site, then inspect the page again after Google recrawls it. When Google returns a link to its full report, **Open in Search Console** shows below the details. It opens in a new tab. ## If something goes wrong [#if-something-goes-wrong] If the URL doesn't start with http\:// or https\://, the form says "Enter a full page URL, like `https://example.com/page`." If Google can't inspect the page, the form says "Search Console could not inspect that URL. It must be a page on", then your property, then "Try again." Check that the URL belongs to that property. If the Google connection isn't set up, the section shows one of these messages. Select **Open the Google connection** to fix it in **Settings** > **Connections** > **Google**: * Not connected: "Connect Google to see search clicks and impressions." * No Search Console access: "Your Google connection does not include Search Console. Reconnect Google and allow Search Console access." * No property chosen: "Choose the Search Console property Mark should read on the Google connection." * Access revoked or expired: "Google access was revoked or expired. Reconnect Google to see search data." If Search Console doesn't answer, the section says "Search Console did not answer. Reload to try again." Reload the page. For the rest of the site's page, see [Site audit](/docs/site-audit) and [Check site readiness](/docs/check-site-readiness). # Install the site tag (/docs/install-the-site-tag) The site tag is a single script you paste on your site. It does two things: it starts analytics for your pages through Mark's own domain, and it listens to the lead form you already have, so the traffic source that produced an enquiry is attributed correctly. It does not create the lead. Your form still submits to your CRM exactly as it does today — the tag only watches for the submission and reports it to Mark's analytics as an attribution event. It does not change your form either: your markup, your styling, your validation, and your thank-you page all stay exactly as they are. ## Paste the snippet [#paste-the-snippet] Copy the snippet from your site's row on the Websites page and put it in the `` of every page, in the order it is given. To find that row, see [Manage websites](/docs/manage-websites). Mark's own line looks like this: ```html ``` If your site uses Google Tag Manager or Cookiebot and Life With Data has your container id or Cookiebot id, the snippet also carries those blocks: Consent Mode defaults first, then the Tag Manager loader, then Cookiebot outside Tag Manager. Mark never writes the consent defaults itself; they are pasted once, here. The snippet appears only once analytics is set to run through your own domain. Until then the Websites page says what is still needed. This sequencing matters: the tag sends events to that domain, so a snippet pasted earlier would sit on your pages doing nothing. The key identifies your site and is safe in client-side code, the same way any analytics key is. If your site already loads PostHog, remove that snippet when you add this one. Two copies of the analytics library on one page split a single visitor into two people, and the lead you care about ends up attached to whichever copy happened to answer first. ## Tell Mark which form to capture [#tell-mark-which-form-to-capture] Life With Data configures this for you, but it is worth knowing what they need. There are two ways the tag can hear about a submission. ### A form in your own markup [#a-form-in-your-own-markup] Most sites. Mark needs a **CSS selector** that matches your form element, for example `#enquiry` or `form.contact-form`. The tag attaches one listener and reads the form's named inputs when it is submitted. These input names become their own property on the attribution event: | Input name | Becomes | | ------------------------------------- | -------------------------------------------------------------------- | | `email` | The visitor's email. Required — a submission without one is ignored. | | `name`, or `firstName` and `lastName` | The visitor's name. A split pair is joined into one. | | `phone` | The visitor's phone. | | `message` | The enquiry text. | | `personaId` | The persona, if your form asks. | | anything else with a `name` | An answer, filed under that input's name. | Password inputs and hidden inputs are never read. If your form has a honeypot field, tell Life With Data its name: the tag drops a submission with a filled one instead of reporting it. ### A HubSpot form [#a-hubspot-form] If your enquiry form is a HubSpot embed, a plain listener cannot see it, so the tag listens for HubSpot's own submission event instead. Life With Data needs to know you use HubSpot, and optionally the **form GUID** if you have more than one HubSpot form and only one of them is your lead form. HubSpot's `email`, `firstname`, `lastname`, `phone`, and `mobilephone` properties map onto the event the same way; every other property on the submission becomes an answer. This works with both HubSpot embed styles. It does **not** work with HubSpot Forms V4, which stopped broadcasting the event the tag listens for — if your embed code mentions V4, tell Life With Data before you install. ### No form yet [#no-form-yet] Leave the capture setting unset. The tag still runs analytics; it simply attaches no form listener and never reports a submission. ## Cookie banners [#cookie-banners] Each site has a **Cookie consent** setting with two choices. **No cookie banner, track every visit** is where every site starts until you give Life With Data a Cookiebot id. The tag starts full analytics on page load and does not wait for a banner to answer. If a banner does ask and the visitor declines, the tag switches to cookieless from then on. **Wait for the cookie banner's answer** is what the site switches to once it has a Cookiebot id. Life With Data can also pick it for a site that runs HubSpot's banner instead. The tag then follows the banner: | Banner | What the tag does | | ----------------------- | ---------------------------------------------------------------------------------------- | | Cookiebot | Waits for statistics consent. Full analytics on accept; cookieless analytics on decline. | | HubSpot's cookie banner | Waits for the analytics category. Full analytics when granted; cookieless when denied. | | Anything else, or none | Starts immediately. | That last row matters. The tag only recognises the banners listed above, so if you use a different consent tool the tag will not see it and will begin capturing straight away. Tell Life With Data which tool you use and support for it can be added. Cookieless means nothing is stored on the visitor's device: no cookie, no local storage. Page views are still counted, keyed by a server-side hash that rolls over daily, so a decline does not make the visit vanish from your numbers. Google's tags follow the same setting. The Consent Mode defaults in the snippet start granted on a site that tracks every visit, and denied on a site that waits. The snippet is pasted once, so if the setting changes, paste the new snippet from the Websites page to bring Google's tags in line. Two details a privacy review usually asks about: * The analytics library is **fetched** before consent is answered, but it is loaded switched off — it sends no events and sets no cookies until consent is granted. That is the analytics vendor's own documented pattern. * If your banner loads more slowly than the tag, a single page view can be recorded before the banner has an answer. Declining still stops everything from that point on, and the recorded view is discarded with the visitor's other data. A visitor who declines still has their submission reported if they go on to submit your form — submitting a form is its own act of consent. The tag keeps the click ids and UTM parameters from the page the visitor landed on in memory and attaches them to the submission, so the source survives even when the banner was declined or an ad blocker dropped the first page view. ## Updates [#updates] The tag is served from Mark, not copied into your pages, so improvements reach your site on their own within a few minutes. You never need to paste it again. The `v1` in the URL only changes if the tag ever changes in a way that needs your attention, and Life With Data would tell you first. ## Turning it off [#turning-it-off] Delete the script tag from your pages and the integration stops immediately; nothing on Mark's side depends on it still being there. If Life With Data needs to withdraw a change to the tag itself, they revert it and every page picks up the correction within five minutes without you doing anything. ## Checking it works [#checking-it-works] 1. Open a page with the tag on it and look at your browser's network panel. You should see requests to the analytics domain on your own site (the one Life With Data set up as the reverse proxy) and none at all to `posthog.com`. 2. Submit your form with a real email address. 3. The `lead_submitted` event appears in your PostHog project, and your own thank-you page and CRM still work exactly as before — the tag only reports the submission for attribution, it never touches either. A submission your own form rejects — failed validation, or a failed bot challenge — is not reported, so your validation stays the gate on what counts. If the form submits but nothing reaches PostHog, the selector is the usual culprit — check it still matches after any redesign. To post a lead to Mark directly instead of relying on the tag — for a form that has no `submit` event, or a vendor the tag has no adapter for — see the [lead capture contract](/docs/capture-leads). # Manage assets (/docs/manage-assets) **Assets** lists every file in your workspace: your uploads, URL imports, and files synced from Google Drive. Open it from the sidebar under **Library** > **Assets**. The newest files are at the top. ## Upload files [#upload-files] 1. Open **Library** > **Assets**. 2. Drag images or video onto the box that says **Drop images or video here, or click to choose files**. Or select the box to pick files from your computer. On a phone there's nothing to drag, so the page shows a **Choose files to upload** button instead. Tap it to pick photos or videos from your phone. Upload several files at once, up to 400 MB each. Each file shows a progress bar while it uploads, and it appears at the top of the table when it's done. The box takes images and video only. Other file types are skipped. ## Read the table [#read-the-table] Each row is one asset: * A small preview. Images show a thumbnail. Video, audio, and documents show an icon for their kind. If an image preview can't load, the tile shows a crossed-out image icon instead of staying blank. * **Name**: select it to open the asset's details. * **Source**: **Google Drive**, **Upload**, or **URL import**. * **Drive path**: the folder path in Google Drive, for synced files. * **Added**: the date the asset came into Mark. ## Filter and page through assets [#filter-and-page-through-assets] Two sets of filters sit above the table. Use them together to narrow the list: * Source: **All**, **Google Drive**, **Upload**, or **URL import**. * Kind: **All**, **Images**, **Video**, **Audio**, or **Documents**. When there are more assets than fit on one page, select **Older** to go further back and **Newer** to come forward again. Paging keeps your filters. Changing a filter takes you back to the first page. If no assets match your filters, the page says **No assets match these filters**. Select **Clear filters** to see everything again. That's different from an empty library, which says **No assets yet** and points you to **Connections** if you want to sync files from Google Drive. ## See an asset's details [#see-an-assets-details] Select an asset's name in the table. The detail page shows a preview and the facts Mark stores about the file. A field only appears when it applies, so an upload has no Drive path and a document has no dimensions. * **Kind**: image, video, audio, or document. * **MIME type**: the file's format, for example image/png. * **Dimensions**: width and height in pixels, for images and video. * **Duration**: for video and audio. * **Size** * **Source** * **Source URL**: where Mark got the file, for URL imports and files synced from Google Drive. * **Drive path** and **Drive modified**: for files synced from Google Drive. * **Added** Select **Open file** to play or download the file in a new tab. Some files, such as video and large files, open through a link that only works for a short time. If you copy that link and open it later, it can stop working. Go back to the asset and select **Open file** again for a fresh one. ## Rename an asset [#rename-an-asset] 1. In the table, open the actions menu at the end of the asset's row. 2. Select **Rename**. 3. In **Rename asset**, type the new name and select **Save**. Renaming changes the name in Mark only. The file itself doesn't change. If you clear the name and save, the dialog asks you to **Enter a name.** ## Delete assets [#delete-assets] To delete one asset: 1. Open the actions menu at the end of the asset's row. 2. Select **Delete**. 3. Confirm with **Delete**. The file is gone from Mark for good. To delete several at once, select the checkbox on each row you want, or the checkbox in the header to select every asset on the page. A bar shows how many are selected. Select **Delete** in that bar, then confirm. Mark won't delete an asset that's still in use: * If the asset is the brand logo or is attached to posts, the dialog says it's in use and nothing is deleted. It lists the brand logo and up to three posts that use it, with each post's channel and status, and a count of any more. * When you delete one asset and only posts in draft or changes requested use it, the dialog offers a button with the post count, such as **Remove from 2 posts and delete** or **Remove from 1 post and delete**. Select it to take the asset off those posts and delete it in one step. * If it's the brand logo, or a post in any other status uses it, that button doesn't show. Other statuses include in review, approved, scheduled, and published. Select the brand logo or each listed post to open it, remove the asset there, then try again. * If anything using the asset changed after you opened the dialog, it says **Something changed since you opened this. Nothing was removed or deleted.** Try again. * In a bulk delete, assets in use and files synced from Google Drive are skipped, and the rest are deleted. The dialog then shows how many were deleted and why each skipped asset stayed. ## Files synced from Google Drive [#files-synced-from-google-drive] Mark can't rename or delete a synced file. Its actions menu says **Synced from Google Drive. Rename or remove it in Drive.** Make the change in Google Drive instead. # Manage websites (/docs/manage-websites) **Settings** > **Websites** shows one site at a time. The site's name at the top is a menu: select it to switch to another site, or to add one. The page opens your primary site. The first site is **primary**. It's the site **Home** shows. Each site has its own snippet and its own readiness score, and every site reports into one analytics project. Until you add a site, the page says "No websites yet. Add one to get its tag, a site audit, and an AI readiness score." Select **Add website** there to add the first one. ## Switch sites [#switch-sites] Select the site's name at the top of the page. The menu lists every site, primary first, with a **Primary** badge on the primary one. Under each name it shows the site's tag status and its readiness score once it has one. Select a site to open it. The tag status says when a page on that site last loaded its tag: * **Tag seen** with how long ago, like "Tag seen 3 hours ago". * **Tag not seen yet**. The snippet is ready, but no page on the site has loaded it. It updates within an hour of the first visit after you paste it. * **Tag not seen in N days**. The tag loaded before, but not in the last week. The snippet may have been removed from the site. * **No snippet yet**. See [When there's no snippet yet](#when-theres-no-snippet-yet). When another site's tag is not seen yet, or not seen in a week, a link next to the site's name says so, like "1 other site needs setup". Select it to open that site. Only pages on the site's own domain, or a subdomain like `www.`, count. A site that tells browsers to send no referrer (`Referrer-Policy: no-referrer`) always reads **Tag not seen yet**, even when its tag works. ## Add a website [#add-a-website] 1. Select the site's name at the top, then **Add website** at the bottom of the menu. 2. Enter the **Hostname**, like `verdella.com`. Use the bare hostname, with no `https://`, port, or path. 3. Optionally enter a **Name (optional)**, like "Main site". The menu shows the name, or the hostname if there's no name. 4. Select **Add website**. The button reads **Adding…** while it runs. The new site opens with its snippet at the top, ready to install. If the hostname isn't valid or the site is already listed, the dialog says so and nothing is added. ## Edit a site [#edit-a-site] Under **Site details**, change the **Hostname** or **Name (optional)**, then select **Save**. ## Make a site primary [#make-a-site-primary] 1. Open the site you want **Home** to show. 2. Select **More actions** (**⋯**) next to **Site audit**, then **Make primary**. The primary site has no **Make primary** option, and it shows a **Primary** badge next to its name. ## Remove a site [#remove-a-site] 1. Open the site, select **More actions** (**⋯**), then **Remove website**. 2. A dialog asks to confirm, like "Remove verdella.com?" 3. Select **Remove website** to confirm, or **Cancel** to keep the site. Removing a site stops its tag working and deletes its readiness, audit, and ranking history. The page then opens your primary site. ## Copy a site's snippet [#copy-a-sites-snippet] Each site has its own snippet under **Tag**. Check the name at the top before you copy, so you install the right site's snippet. 1. Select **Copy**. It always copies the whole snippet. If the snippet is long, select **Show all N lines** to read it, and **Show less** to fold it again. 2. Paste it in the head of every page of that site, in the order given. Mark's tag updates itself. You paste again only after a change to the snippet, covered under form capture below. For the paste steps a site owner follows, and what the capture and consent settings do on the site, see [Install the site tag](/docs/install-the-site-tag). ### When there's no snippet yet [#when-theres-no-snippet-yet] The snippet sends analytics through your own domain, so **Tag** shows no snippet until that's set up. In its place, it shows a notice that says what's missing: * **No snippet yet: PostHog is not attached**. Mark has no analytics project for your workspace yet. * **No snippet yet: reverse proxy not checked**. The project is attached, but Mark hasn't checked whether analytics runs through your domain. Select **Check again**. * **Reverse proxy not enabled** or **Reverse proxy is not live**. Analytics doesn't run through your domain yet. Once it does, select **Check again**. The other notices link to the PostHog connection, where Life With Data finishes the setup. Every site's snippet appears once analytics runs through your domain. ## Set up form capture [#set-up-form-capture] **Form capture** shows what that site's tag reports when a visitor submits a form: the capture method, honeypot field, Google Tag Manager, Cookiebot, and cookie consent. Each site has its own settings, because the form on your main site usually isn't the form on a landing page. 1. Select **Edit capture**. 2. Pick a **Capture method**: * **No form capture**: the tag runs analytics only. * **A form in the site's own markup**: enter the **CSS selector** that matches the form, like `#enquiry` or `form.contact-form`. * **A HubSpot form**: optionally enter the **HubSpot form GUID (optional)**. Leave it blank to capture every HubSpot form on the page. HubSpot Forms V4 isn't supported. 3. Fill in any optional fields the site needs: * **Honeypot input name (optional)**: the name of the site's bot-trap input. A submission with it filled in is dropped. * **Google Tag Manager container id (optional)**: adds Tag Manager to this site's snippet. * **Cookiebot id (optional)**: adds Cookiebot to this site's snippet. 4. Pick a **Cookie consent** setting: **No cookie banner, track every visit** or **Wait for the cookie banner's answer**. Adding a Cookiebot id switches it to wait. You can change it back. 5. Select **Save**. The section shows "Capture settings saved." Tag Manager, Cookiebot, and consent settings change the snippet itself. After you change one, copy the site's snippet again and replace the old one on the site. For what each setting does on the site, see [Install the site tag](/docs/install-the-site-tag). ## Check a site's health [#check-a-sites-health] Select **Site audit** next to the site's name to open that site under **Analysis** > **Website**. The badge next to the name shows the site's latest readiness score. For what that page shows, see [Check site readiness](/docs/check-site-readiness) and [Site audit](/docs/site-audit). # MCP server (/docs/mcp) This site runs a public [Model Context Protocol](https://modelcontextprotocol.io) server at `/api/mcp` — no auth, since everything it serves is already public on this site. Point an MCP client at `/api/mcp` to use it. ## Tools [#tools] * `list_docs` — every page's title, URL, and description. * `search_docs` — case-insensitive text search across all pages. * `get_doc` — a page's full content by URL, as the same Markdown `/llms-full.txt` serves. ## Plain files [#plain-files] For a client that just wants the raw text instead of MCP tool calls: * [`/llms.txt`](/llms.txt) — an index of every page. * [`/llms-full.txt`](/llms-full.txt) — every page's full content. # Read Performance (/docs/read-performance) Performance shows how a workspace did in Google Search and on social over a date range you pick. Open **Performance** in the sidebar. The page has a filter bar, a bar of section links, and three sections: **Search**, **Social**, and **AI assistant traffic**. ## Set the range and filters [#set-the-range-and-filters] 1. Pick a range. Select **Last 7 days** or **Last month**, or set the From and To dates. 2. To see the previous period next to this one, turn on **Compare**. It compares with the window of the same length right before your range. 3. If the workspace has more than one market, pick **All markets** or one market. A market narrows **Social** to that market's posts, and **Search** to that market's country when the country is a 2- or 3-letter code. See [Read Search](#read-search). The line under the **Performance** heading restates what you're looking at: the range, the workspace timezone, the market and pillar if you picked one (the pillar filter sits in **Social**), and the compare window when **Compare** is on. To jump to a section, select **Search**, **Social**, or **AI assistants** in the bar under the filters. It stays at the top of the screen as you scroll. ## Read Search [#read-search] Search shows Google Search Console numbers for the property chosen on the Google connection. The line next to the heading names that property and the last day with data, for example "Data through 2026-09-21, Pacific Time". Google lands data 2 to 3 days late, so the last few days of a range can be empty. If you picked a market, a note under the heading says how Search treats it. Search can filter by the market's country only when that country is a 2- or 3-letter code, like DE or DEU. A name like "Germany" or "DACH" doesn't filter, and neither does "UK". Use GB for the United Kingdom. * If the country is a code, the tiles, **Top queries**, and **Top pages** are for that country. **Tracked rank** isn't filtered. For a market with country DE, the note says "Search shows searches from DE only." * If it isn't, Search covers all markets. The note names the market. For a market called DACH, it says "Search covers all markets. It can filter by country only when DACH's country is a 2- or 3-letter code like DE or DEU." Select **Edit markets** to change the country. The four tiles show **Clicks**, **Impressions**, **CTR**, and **Average position** for the range. With **Compare** on, each tile also shows the previous window's figure as "vs" and the number. ### Top queries [#top-queries] **Top queries** lists the top 100 search queries by clicks. Select **Only positions 5 to 20 (n)** to see only queries that rank on positions 5 to 20, sorted by impressions. The number in the label is how many there are. These queries already rank, just not near the top. Google hides some queries for privacy, so the rows don't add up to the totals in the tiles. If Mark tracks keywords for the first site listed under **Websites**, the table has a **Tracked rank** column. This is Mark's own Google rank check, not Search Console. It shows the latest position and the change since the previous check. That change is not tied to the date range you picked. The note under the table says when the check last ran and has an **Open Rankings** link. ### Top pages [#top-pages] **Top pages** lists the top 100 pages by clicks, with the same four numbers per page. Google counts each page on its own, so a search that shows two of your pages counts for both, and the rows don't add up to the totals. To check how Google indexes one page, open the site's page under **Websites**. Search links to it at the bottom of the section. ### When Search shows no numbers [#when-search-shows-no-numbers] If Google isn't connected or can't be read, Search shows a message instead of numbers. For example, "Connect Google to see search clicks and impressions." Other messages cover a connection without Search Console access, no Search Console property chosen, and revoked access. Fix these in **Connections** > **Google**. The message links there. If Search Console doesn't answer, Search says so. Reload the page to try again. ## Read Social [#read-social] Social covers the posts published in your range, grouped by pillar. Pillars are the content themes set under **Strategy** > **Pillars**. Each pillar with posts in the range has a row, plus a **No pillar** row and a **Total** row. The columns are: * **Posts**: posts published in the range. * **Measured**: how many of those posts have metrics yet. * **Impressions** and **Engagement**: from each post's latest metrics checkpoint. * **Clicks**: clicks on the post's links in the range, not counting bots. The line under the heading says how fresh the metrics are, for example "Data through" and a date, or "No checkpoints yet". To narrow the table, use the pillar filter at the top of the section. Pick **All pillars**, one pillar, or **No pillar**. The market filter at the top of the page also applies here. ### See each post [#see-each-post] 1. Select the arrow next to a pillar to expand it. 2. Each post shows its channel and publish date, with its own numbers. 3. Select the post to open it in Mark. Select the external-link icon next to it to open the live post on the channel. ### Compare with the previous window [#compare-with-the-previous-window] With **Compare** on, the **Clicks** column shows "vs" and the previous window's clicks for each pillar and the total. Only clicks are compared. Impressions and engagement are each post's latest checkpoint, so they don't split cleanly by window. If no posts were published in the range, Social says "No posts published in this range." ## Read AI assistant traffic [#read-ai-assistant-traffic] AI assistant traffic counts fetches of your pages by assistants like ChatGPT, Claude, and Perplexity in the range, with the top pages. It needs a tracking file on your site. See [Track AI assistant traffic](/docs/track-ai-assistant-traffic) to install it and to read what each kind means. # Research and save keywords (/docs/research-and-save-keywords) **Keywords** is where you find search terms worth writing for and keep a list of them. Select **Analysis** > **Research** in the sidebar. **Keywords** is the first tab. It has two views: **Research** gets new keyword ideas, and **Saved** is your list. Each run of keyword research is a paid lookup. Nothing is requested until you select **Run keyword research**. Opening either view, saving, tagging, removing, and tracking are free. ## Research keyword ideas [#research-keyword-ideas] 1. Select the **Research** view. 2. Enter one to five seed terms in **Seed terms (1–5, one per line)**. The line under the box counts them. You'll see "Enter at least one seed term." until you add one. 3. Choose a **Location** and a **Language**. They start at United States and English. 4. Choose a **Result count**: 10, 25, 50, or 100 ideas. 5. Optionally select **Include SERP overview (may increase provider cost)**. It adds a summary of each keyword's Google results, and it costs more. 6. Select **Run keyword research**. The button reads **Running keyword research…** until the results arrive. A line under the button says how many ideas came back and what the lookup cost. If it says "No keyword ideas were returned for these seed terms and settings.", try broader seed terms or another location. ## Read the results [#read-the-results] Each idea has one row: * **Keyword** is the search term. * **Volume** is how many people search for it each month. * **CPC** is the average cost per click for ads on it. * **Competition** is how crowded the ads are for it, as a percentage. * **Difficulty** is how hard it is to rank for it. Higher is harder. * **Intent** is what the searcher is likely after, like informational or commercial. * **SERP** shows **Available** when the SERP overview came back for that keyword. A dash means no figure came back for that keyword. ## Save keywords [#save-keywords] 1. Select the box next to each keyword you want, or the box above the table to select all. 2. Select **Save selected**. A line confirms how many were newly saved, and how many were already saved if any. Keywords save with the location and language of the run. Find them under **Saved**. ## Find saved keywords [#find-saved-keywords] Select **Saved**. The newest saved keywords are at the top. Until you save one, it says "No keywords saved yet. Research keywords and save them here." To narrow the list: 1. Fill in any of these: * **Search**: part of the keyword. * **Tags**: one or more tag names, separated by commas. * **Source**: **All sources**, **Research**, **Domain**, **Agent**, or **Search Console**. **Domain** means you saved it from a domain lookup on the **Competitors** tab. A keyword keeps the source it was first saved from. 2. Select **Apply filters**. Select **Clear** to see the full list again. If nothing matches, you'll see "No saved keywords match these filters." Sort by **Keyword** or **Saved** by selecting the column heading. Select it again to reverse the order. Use **Previous** and **Next** under the table to move between pages. ## Tag saved keywords [#tag-saved-keywords] 1. Select the keywords. 2. Enter a **Tag name**. 3. Select **Add to selected** or **Remove from selected**. To remove one tag from one keyword, select the **×** on the tag in the **Tags** column. ## Remove saved keywords [#remove-saved-keywords] 1. Select the keywords. 2. Select **Remove selected**. 3. Confirm with **Remove**, or select **Cancel**. Removing deletes the keywords, their tags, and any rank history from your saved list. It does not undo. ## Track keywords for rank checks [#track-keywords-for-rank-checks] Tracked keywords get Mark's own Google rank checks for one of your sites. The results show on the **Rankings** tab and on **Performance** > **Search**. See [Read performance](/docs/read-performance). 1. If you have more than one site, pick the rank tracking site from the row of site buttons at the top of **Saved**. 2. Select the keywords. 3. Select **Track selected** or **Untrack selected**. To track or untrack one keyword, select **Track** or **Untrack** on its row. The **Rank tracking** column shows **Tracked** or **Not tracked** for the site you picked. Tracking is free. To run rank checks, see [Check your keyword rankings](/docs/track-keyword-rankings). # Research a competitor's domain and your backlinks (/docs/research-competitors) **Competitors** shows what any domain ranks for in Google, and who links to your own site. Select **Analysis** > **Research** in the sidebar, then the **Competitors** tab. The domain lookup is at the top. **Backlinks** is below it. Opening the tab is free. It shows results you already fetched. These buttons fetch new data, and each press is a paid lookup: * **Look up** * **Look up top pages** * **Keywords** on a row of **Top pages** * **Refresh backlinks** Opening an entry in **Recent lookups** is free. ## Look up a domain [#look-up-a-domain] 1. Enter a domain like `example.com` in **Domain**, or paste a URL. The line under the box shows the domain Mark will look up. 2. Keep or change **Location** and **Language**. The form starts from the lookup on screen. With no earlier lookup, it starts at United States and English. 3. Select **Look up**. **Rank by** sorts the keywords by **Traffic**, **Search volume**, or **Position**. **Keyword contains** is optional. It keeps only keywords with that word in them. If you change a field after a lookup, you'll see "Settings changed — look up again to apply." The results on screen stay as they were until you select **Look up** again. ## Read the results [#read-the-results] The line above the results shows when the data was fetched. The data updates weekly. Three cards sit at the top: * **Estimated organic traffic**: estimated visits per month from Google. * **Organic keywords**: how many keywords the domain ranks for, with counts of new, lost, up, and down. * **Position spread**: a bar chart of ranked keywords by Google position, so it shows how many rank near the top and how many sit further down. Below the cards, the table lists up to 100 ranked keywords: * **Position** is where the domain ranks in Google for the keyword. * **Volume** is how many people search for the keyword each month. * **Difficulty** is "How hard it is to reach the top 10 for this keyword, from 0 to 100. Under 30 is easy, 70 and up is hard." Point at the score to see **Easy**, **Medium**, or **Hard**. * **Traffic** is "Estimated monthly visits this ranking brings the domain, from search volume and the click rate at its position." * **URL** is the domain's page that ranks. ## Save keywords [#save-keywords] 1. Select the box next to each keyword you want, or the box above the table to select all. 2. Select **Save selected**. The keywords save to the location and language shown next to the button. A line confirms how many were newly saved and how many were already saved. Find them under **Research** > **Keywords** > **Saved**, where new ones show **Domain** as their source. ## Look up top pages [#look-up-top-pages] 1. Optionally enter part of a URL in **URL contains**, like `/blog`, to see only matching pages. 2. Select **Look up top pages**. It uses the same domain, location, and language as the form. **Top pages** lists the domain's pages by estimated traffic per month, with how many keywords each page ranks for. To see one page's keywords, select **Keywords** on its row. A panel opens with that page's ranked keywords, by traffic. ## Reopen an earlier lookup [#reopen-an-earlier-lookup] **Recent lookups** lists your workspace's 25 most recent domain lookups, with each one's location, language, **Rank by**, **Contains**, and date. Select one to show its stored results. **Showing** marks the one on screen. ## Check your backlinks [#check-your-backlinks] **Backlinks** shows who links to your own site. If you haven't added a site yet, it says "No website yet." Add one under **Settings** > **Websites** first. See [Manage websites](/docs/manage-websites). 1. If you have more than one site, pick it from the row of site buttons. 2. Select **Refresh backlinks**. Until the first refresh, the line next to the button says there's no backlink snapshot yet for your site. When the refresh finishes, you'll see "Updated just now." The line next to the button shows when the snapshot was captured. ## Read your backlinks [#read-your-backlinks] **Overview** shows the totals for your site: **Backlinks**, **Referring domains**, **Referring pages**, **Rank**, **Broken backlinks**, **Broken pages**, and **Spam score**. Two charts sit next to it: * **Backlink growth** plots backlinks and referring domains at each refresh. It needs at least two refreshes to draw a line. * **Links first seen** counts the links in this snapshot by the month they were first picked up. Under the charts, two tabs split the links: * **Backlinks** lists each link: **Source**, **Target**, **Anchor**, **Flags**, **Link rank**, **Domain rank**, **Spam**, and **First seen**. **Flags** shows **Nofollow** when the link is nofollow. **Spam** is the link's spam score. * **Top pages** lists the pages on your site that get the most links, with **Referring domains** and **Best domain rank** for each. # Review and approve posts (/docs/review-and-approve-posts) Posts waiting for review collect in the **Queue**. Approve several at once from there, or open a post's review page to check it and decide on it alone. For what each status means, see [Review and approval](/docs/concepts/review-and-approval). Open the Queue from the sidebar: **Content** > **Queue**. The page is headed **Waiting for review**. If nothing is waiting, it says "Nothing is waiting for review." ## Filter the Queue [#filter-the-queue] 1. Pick a **Channel**, or leave it on **All channels**. 2. Set **Scheduled from** and **Scheduled through** to limit the list to a date range. Set one, both, or neither. 3. Select **Apply filters**. Select **Clear** to remove the filters. The Queue shows up to 100 posts at a time. If there are more, it says so. Narrow the filters to see the rest. Each row shows the channel, the start of the post, and when it's scheduled. Posts with no time yet show **Not scheduled**. A **Claim flagged** badge means the post has an open claim flag. ## Approve posts in bulk [#approve-posts-in-bulk] 1. Select the box on each post you want to approve. To pick every post in the current list, select the box at the top. 2. Select **Approve selected**. The button reads **Approving…** while it works. 3. Read the summary, like "3 approved, 1 skipped." Under it, each post shows whether it was approved or skipped, and why. Approved posts leave the Queue. Mark skips a post instead of approving it when: * It has open claim flags. The reason lists them. Resolve them first. * Someone already decided on it. * It changed after you opened the Queue. Reload and check it again. * It's no longer in the Queue. * It couldn't be approved for another reason. Try it from its review page. If you select **Approve selected** with no posts picked, the summary says "No posts were selected." ## Open a post's review page [#open-a-posts-review-page] Select a post in the Queue to open its review page. On the **Calendar**, posts that aren't drafts or waiting on changes also open here. See [Reschedule posts on the calendar](/docs/use-the-calendar). The top of the page shows the channel, the status, and the revision number, like **rev 3**. If there's nothing to decide on, the page says **Not in review** instead. The line under it says why, usually one of these: * "This instagram post has no frozen revision yet, so there is nothing to approve. It enters review when it is submitted." The post was written in Mark but hasn't been submitted yet. * "This instagram post was created outside Mark, so it does not go through review here." It's an external post, brought in from the channel. External posts never enter review, whether they're scheduled, published, or failed, so there's nothing to approve, request changes on, or reject. If the post is live on the channel, **View live post** shows under the message. ## Check the post [#check-the-post] Before you decide, check each part of the page. **Media**, **What we will send**, **History**, and **Comments** start collapsed. Open them to see what's inside. * **Preview** shows the post as it will look on the channel. For channels with a length limit, a line under it counts the caption's characters. * **Delivery** shows the time in the workspace timezone (**Scheduled (tenant)**) and in yours (**Scheduled (yours)**), plus the **Account** it posts from. If no account is set up for the channel, it reads "No account configured". Channel fields like **First comment** are listed here too. * **Claims (N open)** lists open claim flags. "No open claim flags on this post." means there are none. * **Media (N)** lists each image or video with its dimensions. If the shape doesn't fit the channel, you'll see "Ratio mismatch." or "Possible ratio mismatch." This is a warning only. It doesn't block approval. * **What we will send**, when it's there, shows the exact content Mark sends to the channel. ## Decide [#decide] The **Decision** box sits beside the post, or at the bottom of the screen on a phone. * If the post has open claim flags, **Approve** is turned off and the box says "Approve is blocked by N open claim flag(s)", followed by the flags. Resolve the flags first. Otherwise, select **Approve**. * To send the post back, select **Request changes**. Fill in **What needs to change?**, then select **Submit**. * To turn the post down, select **Reject**. Fill in **Why is this rejected?**, then select **Submit**. The note is required for **Request changes** and **Reject**. Select **Cancel** to go back without deciding. Once your decision is saved, the box says "Decision recorded." ## Comment and read the history [#comment-and-read-the-history] To leave a comment, open **Comments (N)**. Type in **Write a comment**, then select **Post comment**. Each comment shows who wrote it, the revision, and when. Open **History (N revisions)** to see each revision, who edited it, and when. Older revisions read "Differs from rev N in:" followed by the fields that changed. If the body changed, you'll also see the lines that changed since the previous revision. Under **Decisions**, each past decision is listed with who made it, when, and their note. ## Edit or reschedule [#edit-or-reschedule] The top of the review page has two more actions. Saving an edit or a new time sends the post back to review, so someone has to approve it again. * **Edit** opens the post in the editor. * **Reschedule** asks for a **New time (your timezone)**. Pick the date and time, then select **Reschedule and reopen review**. ## View the live post [#view-the-live-post] Once a post is published, the review page shows **View live post**. It opens the post on the channel in a new tab. # Set KPI targets (/docs/set-kpi-targets) A KPI target is a goal for one metric in one month. Open **Strategy** > **Targets** in the sidebar to set them. Targets are no longer on **Performance**, and no report compares your actual numbers against them. ## Add a target [#add-a-target] 1. Pick a **Metric**: **Posts per week**, **Impressions**, **Engagement rate**, **Leads**, **Qualified**, **Rejection rate**, or **Cost per lead**. 2. Pick a **Month**. It defaults to the current month in your workspace's time zone. 3. Enter a **Value**: zero or a positive number with up to 4 decimal places. 4. Select **Save target**. The target appears in the table below the form, with columns for **Month**, **Metric**, and **Target**. Until you save one, the page shows "No targets yet." ## Change a target [#change-a-target] Save the same metric and month again with a new value. It replaces the old one, so there's no separate edit button. ## Delete a target [#delete-a-target] 1. Select **Delete** on the target's row. 2. Confirm with **Delete**. The dialog warns "This cannot be undone." # Set up posting slots (/docs/set-up-posting-slots) A posting slot is a weekday and a time when a channel posts. Open **Content** > **Slots** in the sidebar to set them. The page heading is **Posting slots**, with the line "The weekly grid of times each channel posts. Times are your organization's wall clock." ## Add a slot [#add-a-slot] 1. Pick a **Channel**. The list starts on **Select a channel**. 2. Pick a **Weekday**. The list starts on **Select a day** and runs Monday through Sunday. 3. Enter a **Time** in the time field. 4. Select **Add slot**. The slot appears in the list below the form, and the form clears for the next one. If Mark can't add it, the form shows why: * "Pick a channel, a weekday, and a time." Mark couldn't read one of the three. Check them and try again. * "That channel already has a slot at this weekday and time." The slot exists already. * "You must be signed in to an organization." Your session ended. Sign in, pick your organization, and try again. ## Read the list [#read-the-list] Until you add one, the page shows "No slots yet. Add one above to say when a channel posts." Slots are grouped under each channel's name, with underscores shown as spaces (so landing\_page reads as **Landing Page**). Each row reads as a weekday and a 24-hour time, like "Monday at 09:00", with a **Delete** button. Empty slots show up on the calendar as **Missed**, **Gap**, or **Open slot**. To read them there and move posts into them, see [Reschedule posts on the calendar](/docs/use-the-calendar). ## Delete a slot [#delete-a-slot] Select **Delete** on the slot's row. There's no confirm dialog, so the row is gone as soon as the page refreshes. # Set up your brand, personas, pillars, and markets (/docs/set-up-your-strategy) Your posts and angles build on your brand, personas, pillars, and markets, so set these up first. Each has its own page in the sidebar under **Strategy**: **Brand**, **Personas**, **Pillars**, and **Markets**. Many fields are lists that take one entry per row. Select **Add** plus the field name (for example, **Add Tone**) for another row, and the **×** next to a row to remove it. Create personas and pillars before you create **Angles**, because angles build on them. [**Targets**](/docs/set-kpi-targets), the last page in the group, has its own guide. ## Brand [#brand] Brand holds the facts and voice your posts should follow, plus your visual identity. The page has two groups, **Brand and voice** and **Visual identity**, and each one is saved and confirmed on its own. 1. Open **Strategy** > **Brand**. 2. In **Brand and voice**, fill in: * **Positioning**, **Differentiators**, and **Doctrine**. * Under **Voice**: **Tone**, **Principles**, **Do**, **Don't**, **Example posts**, **Hashtags**, and **CTA defaults**. * Under **Company facts**: **Company name**, **Website**, **Headquarters**, **Product range summary**, **Factory locations**, and **Languages**. 3. Select **Save and confirm** at the bottom of the group. The page shows "Saved and confirmed." 4. In **Visual identity**, set the **Logo**, **Favicon URL**, **Colors**, **Heading font**, and **Body font**. 5. Select **Save and confirm** at the bottom of that group. To set the logo, select **Choose logo**, pick one image or upload one, and select **Done**. To clear it, select the **×** next to the logo. Either way, select **Save and confirm** for **Visual identity** to keep the change. ### Confirmed or not confirmed [#confirmed-or-not-confirmed] Saving a group is how you confirm a person has reviewed its values. Under each group's name, the page shows "Confirmed" with the date and time of the last save, or "Not confirmed" if nobody has saved that group yet. Saving one group doesn't touch the other. Saving **Brand and voice** leaves **Visual identity** and its confirmation as they were, and the other way around. ## Personas [#personas] A persona is an audience your content is built to help and persuade. 1. Open **Strategy** > **Personas**. 2. Select **Add persona**. 3. Fill in: * **Name**: what you call this audience. * **Slug**: a short, unique label for the persona. * **What they want**: the outcome this audience is after. * **What convinces them**: one entry per row. * **Objections**: one entry per row. 4. Leave **Active** checked unless you're adding it for later. 5. Select **Save persona**. The page shows "Persona saved." To change a persona, select **Edit** on its row, update the fields, and select **Save persona** again. ## Pillars [#pillars] A pillar is a theme your content returns to, with the formats that make it useful. 1. Open **Strategy** > **Pillars**. 2. Select **Add pillar**. 3. Fill in: * **Name**: the theme. * **Slug**: a short, unique label for the pillar. It's fixed after you save. * **Formats**: the kinds of posts that suit this theme, one per row. * **Notes**: anything else the team should know. 4. Leave **Active** checked unless you're adding it for later. 5. Select **Save pillar**. The page shows "Pillar saved." To change a pillar, select **Edit** on its row, update the fields, and select **Save pillar** again. Each pillar's row shows its slug and formats, or "No formats yet". ### A pillar's slug is fixed [#a-pillars-slug-is-fixed] After you save a pillar, **Edit pillar** shows the slug as text instead of a field. The app explains why: "Set when the pillar was created. Tracking links already built from it carry this slug as their campaign, so changing it would split this pillar's reporting in two." Pick the slug with care when you create the pillar. ## Markets [#markets] A market is a place you prioritize, with the local time zone it uses. 1. Open **Strategy** > **Markets**. 2. Select **Add market**. 3. Fill in: * **Name**: what you call the market. Each market needs its own name. * **Country**: where it is. * **Time zone**: the market's local time zone, such as Europe/Berlin. * **Priority**: a whole number. The list shows lower numbers first. 4. Leave **Active** checked unless you're adding it for later. 5. Select **Save market**. The page shows "Market saved." To change a market, select **Edit** on its row, update the fields, and select **Save market** again. Each market's row reads like "Germany · Europe/Berlin · Priority 1". ## Deactivate or delete [#deactivate-or-delete] Personas, pillars, and markets work the same way when you're done with one. * **Deactivate** keeps the item but marks it **Inactive**. Inactive items move to the bottom of the list. Select **Reactivate** to bring one back. * **Delete** removes it for good. Mark asks you to confirm first, because it can't be undone. Mark refuses to delete an item while something still uses it, and tells you to deactivate it instead: * A persona or pillar can't be deleted while posts or angles still use it. * A market can't be deleted while posts or answer-engine checks still use it. When you see "Can't delete this persona because posts or angles still use it. Deactivate it instead." (or the same message for a pillar or market), select **Cancel** and use **Deactivate**. # Site audit (/docs/site-audit) A site audit crawls up to 50 pages of one website with JavaScript turned on, the way a browser sees them, and scores what it finds together with the site's latest AI readiness scan. Select **Analysis** > **Website** in the sidebar to find the **Site audit** report. ## Run an audit [#run-an-audit] 1. Select **Analysis** > **Website**. It opens your primary site. If you have more than one site, select the site's name at the top to switch. 2. In **Site audit**, select **Run audit**. After the first audit the button reads **Run audit again**. While the audit runs, the report shows **Crawling with JavaScript**, how many of the 50 pages are done, and a progress bar. **Checked** shows how long ago Mark last asked the crawler for progress. The page updates on its own every few seconds while it's open. You can't start a second audit on the same site while one is running. The report header shows when the audit finished, how many pages it crawled, and when AI readiness was last scanned. ## Run audits on a schedule [#run-audits-on-a-schedule] Each audit is a paid crawl, so a site starts on **Manual** and audits only when you select **Run audit**. To audit it once a week instead: 1. In **Site audit**, under **Schedule**, choose one: * **Manual: only when you click Run audit** * **Weekly: runs a paid audit once a week** 2. Select **Save schedule**. With **Weekly**, **Next audit** shows the date and time of the next audit, one week after you save. With **Manual**, it says **Manual**. Saving a schedule doesn't start an audit, and selecting **Run audit** doesn't move the next scheduled one. A scheduled audit shows up in the report the same way as one you started. If it fails, the next one runs a week later. It isn't retried sooner, since every retry is another paid crawl. Select **Try again** to rerun it yourself. ## Read the health score [#read-the-health-score] The health score runs from 0 to 100. Next to it are seven category tiles, each with its own score and a count of issues per severity: * **Crawlability & indexing**: broken pages, redirects, and canonicals. * **Rendering**: whether pages show their content without JavaScript. * **On-page content**: titles, descriptions, headings, and text. * **Structured data**: errors in the page's schema markup. * **Performance & page weight**: load time, page size, and compression. * **Security**: HTTPS links. * **AI readiness**: the issues from the site's AI readiness scan. Each issue lowers the score by its weight: a Critical issue counts most, then Warning, then Notice. An issue on every page costs its full weight, and an issue on a few pages costs a share of it. A category with nothing checked shows **n/a** and doesn't count toward the health score. The site's page shows the health score as a badge next to the site's name. Select a tile to show only that category's issues. Select it again, or select **Show all**, to see every issue. ## Severity [#severity] Every issue carries one of three words: * **Critical**: the page or site is broken, or search engines can't read it. For example, a page that returns an error, or a site that shows a blank page without JavaScript. * **Warning**: hurts how search engines read or reach the page. For example, a missing meta description or a slow page. * **Notice**: hygiene. For example, images without alt text. AI readiness issues use the same words: essential is Critical, recommended is Warning, and bonus is Notice. ## Work through the issues [#work-through-the-issues] The list has two views. **By issue** shows one row per check, Critical first. Within a severity, issues that affect the whole site come first, then the ones on the most pages. The **Pages** column says how many pages have the issue, for example "7 of 50", or **Site-wide**. Select a row to see: * what the audit found, in plain words; * one sentence on how to fix it, with a **Learn how** link to a public reference page; * up to 10 of the affected pages, then how many more there are. For an AI readiness row, the link opens the full AI readiness report instead. **By page** lists the pages that have issues, worst first. Select a page to see its issues. Site-wide issues aren't in this view. Both views show 10 rows at a time. Use **Previous** and **Next** below the list to see the rest. Under both views, the report counts the pages with no issues, for example "12 pages had no issues". ## See what changed since the last audit [#see-what-changed-since-the-last-audit] When you rerun an audit, the report compares it with the finished audit just before it. Audits that are still running or that failed are skipped. Above the health score, one line gives the result, for example "Since the audit on Sep 17, 2026: 3 new issues · 4 fixed". If nothing changed, it reads "No new or fixed issues since the audit on Sep 17, 2026". * In **By page**, a page with new issues shows how many next to its issue count, for example "2 new". Open the page and each new issue has a **New** badge. * **Fixed since** lists each issue that went away, as the page address and the issue. Select it to open the list. It's hidden when nothing was fixed. Only pages crawled by both audits are compared. The 50-page limit can reach different pages from one run to the next, so an issue on a page that only one audit crawled counts as neither new nor fixed. That includes a page you published since the last audit. Site-wide issues aren't compared either. The first audit of a site has nothing to compare with. Neither does an audit run after the site's address changed, or one that shares no crawled page with the audit before it. Those show no comparison. While a new audit runs, **Last finished audit** keeps its own comparison. ## The blank page alert [#the-blank-page-alert] If search engines that don't run JavaScript see a blank page, a red alert sits at the top of the report. It says how many sample pages were fetched without JavaScript and how to fix it: serve each page's own HTML through prerendering or server rendering. Select **See the issue** to jump to its row. This is the most damaging finding an audit can report. Until it's fixed, those search engines see the same empty page at every address on the site. ## Check AI readiness [#check-ai-readiness] The **AI readiness** tile has a **Check now** button. It runs a new AI readiness scan and rescores the report. When it finishes, the tile says "Updated just now." If the scan can't run, it says "Could not check right now. Try again in a few minutes." and keeps the last results. A site that has an AI readiness scan but no audit yet shows the AI readiness tile and its issues on their own. Select **Run audit** to add the crawl. ## When an audit doesn't finish [#when-an-audit-doesnt-finish] If the crawl fails, the report says **The audit didn't finish** and shows why, for example when the site's robots.txt blocks the crawler, the site can't be reached, or the crawl timed out. Fix the cause if there is one, then select **Try again** to run a new audit. While a new audit runs, or after one fails, the last finished audit stays below it under **Last finished audit**, with the date it finished. Select it to open it. Audits that ran before site-wide checks existed show the issues they found on each page, without site-wide rows. # Track AI assistant traffic (/docs/track-ai-assistant-traffic) AI assistants like ChatGPT, Claude, and Perplexity fetch web pages, but they don't run the site tag, so the tag never sees them. A small file on your site records each fetch in your PostHog project. **Performance** then shows the hits in its **AI assistant traffic** section. A hit means an assistant fetched a page. It doesn't mean the assistant cited or recommended you. ## Before you start [#before-you-start] * The site runs Next.js 16 or later. Other site builders aren't supported yet. * PostHog is connected in **Connections** > **PostHog**. Without it, the Website connection shows "Connect PostHog" instead of the file. ## Install the file [#install-the-file] 1. Open **Connections** > **Website**. 2. Under **AI assistant tracking**, pick the website if you have more than one. 3. Select **Copy** on the file. 4. Save it as `proxy.ts` at the root of the site's project and deploy. If the site already has a `proxy.ts`, the comment at the top of the file says how to merge the two. The file already holds your PostHog project's public token, the same one the site tag uses. Copy a fresh file from Mark rather than editing it by hand. ## Check the install [#check-the-install] Select **Check AI assistant tracking**. Mark loads the site's home page as **Mark-Probe**, then waits up to 30 seconds for that visit to reach PostHog. Mark-Probe is Mark's own check. It never counts as an assistant. | Result | What it means | | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | **Receiving** | An AI assistant fetched a page in the last 7 days. | | **Installed** | Mark's check reached PostHog. No assistant has visited in the last 7 days yet; on a small site that can take days. | | **Not installed** | The site answered, but nothing reached PostHog. Deploy the file, or check again in a minute if you just did. | | **Blocked by the site's firewall** | The site refused Mark's check, so Mark can't tell. Allow the Mark-Probe user agent in the site's firewall rules, then check again. | | **Site didn't answer** or **Site couldn't be reached** | Mark couldn't load the home page. Check that the site is up. | | **Check failed** | Mark couldn't read your PostHog project. Check again in a few minutes. | The result shows for the website Mark last checked. Pick another website and select the button again to check that one. ## Read AI assistant traffic [#read-ai-assistant-traffic] Open **Performance** and pick a range. The **AI assistant traffic** section shows: * **Hits**: every assistant fetch in the range. * Hits per assistant, split by kind: * **Crawler** collects pages for training or an index (GPTBot, ClaudeBot, PerplexityBot). * **Search** builds the assistant's own search results (OAI-SearchBot, Claude-SearchBot). * **User fetch** loads a page because a person asked the assistant about it (ChatGPT-User, Claude-User, Perplexity-User). * **Top pages**: the 10 pages fetched most. * A "Data through" line with the newest hit. With no hits in the range, the section says so and links to the Website connection. Counts come from the user agent each assistant sends. Mark doesn't verify it, so anyone can send a request that claims to be GPTBot. # Check your keyword rankings (/docs/track-keyword-rankings) **Rankings** shows where each tracked keyword ranks in Google for one of your sites. Select **Analysis** > **Research** in the sidebar, then the **Rankings** tab. Opening the tab is free. It shows positions from checks that already ran. Only two things spend money: **Run now** and a **Weekly** schedule. ## Before you start [#before-you-start] Rankings only checks keywords you track. Track them first from **Research** > **Keywords** > **Saved**. Tracking is free. Until you track a keyword, the tab says no keywords are tracked yet for your site and links to Saved keywords. ## Pick a site and locale [#pick-a-site-and-locale] If you have more than one site, choose it in the site picker at the top. If a site is tracked in more than one locale, pick the locale from the row of buttons under it. The box above the table shows the **Site**, **Locale**, **Last checked**, and **Next check** for what you picked. ## Check positions now [#check-positions-now] 1. Select **Run now**. You'll see "Check started." If a check is already running, you'll see "A check is already running. Showing its progress." 2. Watch the progress line under the button, like "Checking 12 keywords: 5 done, 1 failed." The page refreshes on its own until the check ends. 3. Read the result line when it finishes: * "Last check finished for all 12 keywords." Every keyword got a new result. * "Last check finished with 2 of 12 keywords failed." The failed keywords show **Check failed** in the table. Run the check again to retry them. * "Last check failed. No new positions were recorded." Every keyword shows **Check failed**. Earlier positions stay in each keyword's history. Run the check again later. ## Set a schedule [#set-a-schedule] 1. Under **Schedule**, choose one: * **Manual: only when you click Run now** * **Weekly: runs a paid check once a week** 2. Select **Save schedule**. With **Weekly**, **Next check** shows the date and time of the next check. With **Manual**, it says **Manual**. Saving a schedule doesn't start a check. ## Read the table [#read-the-table] Each tracked keyword has one row: * **Position** is where your site ranked in Google on the last check, like **#4**. * **Change** compares the last two checks: **Up 3**, **Down 2**, or **No change**. It shows a dash unless both checks found your site. * **Ranking URL** is the page on your site that ranked. * **Last checked** is the date and time of the keyword's latest check. **Position** can also show one of these: * **Not checked yet**: this keyword has never been checked. * **Not in top 100**: the check ran, and your site wasn't in the first 100 Google results. * **Check failed**: the latest check couldn't get a result for this keyword. Sort by **Keyword** or **Position** by selecting the column heading. ## See a keyword's history [#see-a-keywords-history] Select a keyword's name to open its history under the table. Once the keyword has ranked in at least two checks, a chart shows its position over time, with #1 at the top. The **Checked** list shows every check with its position and ranking URL. Select **Close** to hide it. # Reschedule posts on the calendar (/docs/use-the-calendar) Move a post to a different posting slot on the **Calendar** by dragging it or by picking a slot from a list. The calendar shows one week of posting slots and the posts in them. Open it from the sidebar: **Content** > **Calendar**. Use **Previous week** and **Next week** (the arrows at the top) to change weeks, and **Today** to come back to the current one. The week's dates and your workspace timezone sit next to the arrows. ## Read the week [#read-the-week] The calendar has one row per channel, with a column for each day. Under each channel name, the row shows progress against its cadence, like "2 of 3 this week". A channel with no cadence shows a count instead, like "2 committed posts". Each post card shows the post's time, its status, the first line of the post, and its media. A dashed card is an external post published outside Mark, tagged with where it came from. A card marked **Off-slot** is a post whose time doesn't match any of the channel's slots. Slots with no post show one of three labels: * **Missed**: the slot's time has passed and nothing went out. * **Gap**: the slot is in the next 7 days and has no post yet. * **Open slot**: the slot is more than 7 days away. Slots come from **Content** > **Slots**. ## Move a post [#move-a-post] A post can move to an empty future slot on its own channel, in the week you're looking at. Published posts, external posts, and posts with changes requested have no move controls. To drag a post: 1. Grab the handle on the post card. 2. The slots that can take it light up and show **Move here**. 3. Drop the post on one of those slots. To move a post without dragging: 1. Open **Move to slot…** on the post card. It lists only the slots that can take the post. 2. Choose a slot. 3. Select **Move**. The calendar refreshes with the post in its new slot. ## What moving does to review [#what-moving-does-to-review] If the post is a draft, it only gets the new time. Nothing else changes. Any other post goes back to review with the new time. That includes posts that were already approved or scheduled, so someone has to approve it again before it can publish. For how review works, see [Review and approval](/docs/concepts/review-and-approval). ## When a move is refused [#when-a-move-is-refused] If Mark can't make the move, the calendar shows why under the week: * "Choose a slot on the same channel." A post can only move within its own channel. * "Choose an empty future slot." The slot's time has passed. * "That posting slot is already occupied." Another post took the slot first. Pick a different one. * "Published posts are read-only." The post already went out. * "External posts are read-only." The post was published outside Mark. * "This post changed. Refresh the calendar and try again." Someone else changed the post while you were moving it. Refresh the page and move it again. * "That post or posting slot is no longer available." The post or the slot was deleted. Refresh the page. # Concepts (/docs/concepts) Mark is multi-tenant. Life With Data and each client run in separate workspaces on the same platform. * **Workspace** — one tenant's data: its content, its schedule, its connections to systems like HubSpot, Google, or the Instagram and X accounts it publishes to. Life With Data is tenant one, so nothing waits on a client grant to ship. * **Draft** — a piece of content (a post, an email, an ad) before it publishes. Drafts carry claims that need a confirmed source before they can be approved. * **Review and approval** — see [Review and approval](/docs/concepts/review-and-approval) for how a draft moves from written to published. Mark maps onto a client's existing systems. It does not invent new fields, statuses, or lists in a client's CRM. # Review and approval (/docs/concepts/review-and-approval) Nothing publishes without a person confirming it. That is the loop Mark is built around: draft, in review, approved, scheduled, published — or changes requested and back to draft. A draft that cites an unconfirmed claim gets flagged, and the flag blocks approval until someone confirms the source. It gets better with each review and approval. Who reviews depends on the workspace. In a client's tenant, that client is the approver — the UI names them (`Waiting on Verdella`), not a generic pending state. ## Emails approvers get [#emails-approvers-get] Mark emails workspace members when a post in review needs attention. Each post in an email has a **Review post** button that opens its review page. * **Posts waiting for review** is a daily digest. It says "Verdella has 3 posts in review." and lists each post by channel with its scheduled time, or **No time set**. When a post is due soon, the list starts under **Due within 24 hours**. * **A post needs your review** covers one post that's due within 24 hours. Some workspaces get this in place of the daily digest. * **A post missed its scheduled time** means the post was still in review when its time passed, so it didn't publish. Open it to decide what happens next. The member who submitted a post doesn't get the digest or the due-soon email about it. A missed post emails every workspace member, including the person who submitted it.