Skip to main content
A page is a small app Erdo builds for you: a dashboard, a report, an interactive tool. It’s wired to your data with live values, runs in a managed sandbox under your permissions, and can be shared with your team or published.

Getting a page

You don’t drag and drop — you ask. In a conversation, describe what you want and Erdo builds it:
“Turn this into a dashboard showing signups by plan over time, with a filter by region, that I can share with the team.”
Erdo generates the page, wires it to the right data, and opens the live Page beside the conversation controlling it. Selecting a Page in the Pages library replaces the library rail with that conversation, so the Page remains the dominant canvas while you ask for a change and watch it update live. A single Page has no output selector or details panel competing for space. On a smaller screen, one Page / Conversation control switches between the two views because they cannot fit side by side. Use Page width above the live Page to check its responsive layout at Full width, Tablet (768 px), or Mobile (390 px). Tablet and Mobile set the Page’s real CSS viewport width, so responsive breakpoints run exactly at that width; they do not pretend to emulate a particular device, browser, touch screen, or user agent. If the selected width is wider than the workspace, scroll the canvas sideways or open it fullscreen. Open page fullscreen expands the Page itself over the workspace without starting a second copy or losing its current state. The width choice remains in effect, and you can return with Exit page fullscreen in the top-right or by pressing Esc, including while focus is inside the Page. The same Page canvas is used when a Page is selected as a Thread output; the Thread does not add a second workspace-fullscreen control beside it. When you return to a Page, use the conversation beside it to ask for a change. The full conversation composer is available from the first message, including attachments, skills, effort, and approval controls. When that conversation pane is narrow, response depth and approvals move into the Message options drop-up instead of squeezing the message field. The Page workspace starts a fresh, Page-specific amendment conversation only when you send, with the Page, the version you were viewing, and its recent change log attached as structured context. This lets each new conversation know what was already changed and why, so it won’t undo a decision an earlier revision deliberately made. Erdo reads the current Page before writing, so the new conversation has the context it needs without replaying an old, unrelated transcript. The Page remains the same durable output and each saved change becomes another revision in its history — a save that changes nothing adds no revision, so the history stays a log of real changes. The conversation beside the Page is its single amendment control. Opening a Page from a conversation, the Pages list, or a recent-Page link always arrives in this same focused workspace. When one conversation produces several durable artifacts, artifact tabs appear in that Thread and remain available after a reload; a single artifact does not get a one-item tab strip. Links labelled Source conversation in Version history are provenance: they open the transcript that produced that saved version. The Page workspace never silently reuses one for the next amendment; deliberately continuing that source conversation retains its older transcript, while returning to the Page starts from clean, current Page context. Agent-created revisions record the conversation and agent run that produced them. When you can access that source conversation, use Version history on the Page to move from the saved version back to its conversation or execution details. This keeps prior reasoning available without making every future amendment carry it in the model context. Sharing an amendment conversation also shares its Page editing context, and that access remains attached to the conversation when newer amendments are created. For larger builds, the originating Thread shows each durable artifact and the Workstream’s parallel responsibilities as planned, working, in review, or accepted. A worker finishing is not the same as Erdo accepting its result: candidate Pages stay private until the Workstream has reviewed and adopted them, and publishing remains a separate approval-gated step.

Edit a spot

Most changes to a page are about one particular thing on it — this image is cropped, this heading should say something else, this button is the wrong colour. Describing where that thing is takes longer than saying what should change, and it can go wrong: a page that says “Book a tour” in three places gives Erdo no way to know which one you meant. So point at it instead. Edit a spot above the live Page turns the cursor into a pointer; hover to see what you’re about to pick out, then click it. A small box opens where you clicked. Type what should change and press Enter. Erdo edits exactly that element and nothing else, and answers in one line. Your instruction goes into the conversation beside the Page like any other message, marked with the spot it was about, so the whole run of small fixes stays readable afterwards. If there’s no conversation open yet, sending starts one. Two things worth knowing:
  • The Page can’t be clicked through while you’re picking — links don’t follow and forms don’t submit. Press Esc to put the cursor back to normal.
  • Pick a spot at the Page width you’re complaining about. If something is cropped on Mobile (390 px), point at it there and Erdo fixes the mobile layout rather than changing what everyone else sees.
Edit a spot is available on the Page beside its conversation. A published page opened by a visitor is a page, not an editor.

Pinning pages to a project

When a conversation belongs to a project, every page it creates is added to that project’s recent outputs automatically. This keeps the result discoverable without changing how you work in the conversation. Pin the pages that should define the project from the pin button in the page toolbar, or from Recent pages on the project home. Pinned pages appear at the top of the project home and in the project’s sidebar. Unpinned outputs stay available in Pages and Recent pages, so generating a one-off output does not force it into a project.

Live, not a snapshot

Pages read your data at view time, so they stay current — open one tomorrow and it reflects tomorrow’s numbers. They can also hold their own state and react in realtime when more than one person is looking.

How old is this page’s data?

The strip above every page answers that, and how it phrases the answer depends on what keeps the page’s data current.
  • Data updated N ago — at least one of the page’s datasets has a scheduled refresh, and this is the oldest of those refreshes. The refresh button beside it re-runs them. If one is overdue or its last run failed, the strip says so instead and turns amber; if the connection behind it needs reconnecting, it links you to the connectors page.
  • Data written N ago · Written by uploads, pipelines, and automations — nothing on the page refreshes on a schedule, but something writes its data anyway: an event pipeline landing form submissions and page events, an automation writing rows as it runs, a file upload, or a connected integration’s sync. There is nothing to press refresh on — the data arrives when it arrives — so the strip reports the last time it did.
  • Page updated N ago · Static data — nothing has ever written the page’s data, so the only thing that has changed is the page itself.
This includes uploads and successful row writes to custom PostgreSQL and ClickHouse storage. Replacement uploads replace the current rows instead of appending to an earlier upload. ClickHouse deletions wait for completion before they count as a fresh write. For synced integrations, the recorded sync time counts only while that sync is the dataset’s active data source. Disabling sync removes its historical write time from the freshness calculation. Freshness records completed storage operations; it does not promise that every operation changed a row value. The data freshness strip shows Checking data freshness… while it reads and Data freshness unavailable if the check fails. Use Retry freshness check to try again; the Page itself remains available. A failed check is never labelled Static data. Freshness follows the same data permissions as the Page.

Self-healing

A page is real code wired to data that changes over time, so either side can drift: the code can hit a case that only shows up later, or a dataset the page reads can be reshaped by a refresh weeks after the page was built. Erdo keeps pages working without you having to watch them. When Erdo builds or edits a page, it validates the code and its data queries before the page is saved. It re-verifies every page after each dataset refresh, so a change to your data can’t quietly break a chart. And it monitors your published and shared pages for errors as real visitors view them. When something breaks at any of those points, Erdo automatically diagnoses the problem and prepares the fix — usually before you’d have noticed. A fix to a private draft is applied on its own; a fix to a published page is proposed for your approval first (or applied automatically if you’ve set an approval policy for page edits), so a live page never changes without your say-so. Every fix is a new revision, so nothing is lost.

Sharing

  • Private — open to people in your workspace, under your existing permissions.
  • Public link — publish a read-only version anyone with the link can view.
Identity and access always come from the viewer’s own permissions, so a page can’t expose data the viewer isn’t allowed to see.

Editing a published page keeps it live

Before a page goes public, Erdo checks it: the source must pass validation (for example, no images hotlinked from another site), and a real mobile browser must render it without sideways scrolling or content a visitor can never see. A published page goes through the same check every time it changes, whether an agent edits it, you edit it through the API or MCP, or you restore an older version. A change to a published page never takes it offline. Either the new version passes and goes live, or visitors keep getting the version they had:
  • A change that adds a problem is not saved. The published page stays exactly as it was, and the reply names each problem the change would have added and where it is, so it can be fixed in the same change.
  • A change that fails the browser check is undone. The previous version goes back up, still public, and Version History records the revert and why.
  • Problems the published page already had don’t block a change. A page published before a rule existed can still be edited. The change only has to avoid adding new problems, and the old ones are reported so they can be fixed.
If a published page ever can’t be put back online, Erdo raises an urgent item in your attention feed naming the page. Publish it again from the Share menu to bring it back.

Images before you publish

A page’s brand images are part of your Knowledge library, and publishing the page is what makes them public. Until then they are still private — which used to mean a page looked broken in exactly the moment you were deciding whether to publish it. It doesn’t any more. When you open an unpublished page in Erdo, its images load for you, so what you review is what visitors will get. The same is true of the automated reviews that read a page before it goes live: they see the real photography rather than a page full of gaps. What that doesn’t cover is anyone outside. Send a colleague the link to a page you haven’t published, or open it in a private window, and the images won’t be there — publishing is still the moment a page’s media becomes viewable to the world. If someone reports blank images on a page you shared with them, publish it and they’ll appear.

Managing pages

Pages have a full lifecycle you can drive from a conversation, the CLI, the REST API, or MCP:
  • List the pages in your workspace, newest first, and filter by title or a created-at window to find a specific set. In a conversation, each result says whether it is public and includes the exact visitor URL only when it is; branded custom-domain URLs are returned verbatim rather than reconstructed.
  • Delete a page. This is a soft delete: its public share link stops working immediately and it drops out of listings, but it is not gone.
  • Restore a deleted page to bring it back. It returns private — deleting revoked its public grant, so re-publish it if you want the share link live again.
In a conversation you just ask (“list my pages”, “delete the old pricing page”). From the terminal:
The same operations are on the REST API (GET /v1/pages, DELETE /v1/pages/:id, POST /v1/pages/restore/:id) and as the MCP tools erdo_list_pages, erdo_delete_page, and erdo_restore_page.

Cloning a page

When a page already works, the fastest way to a variant is to copy it rather than rebuild it. Cloning takes a page you can see and gives you a second, independent page with the same content — the same HTML, styles, and script, byte for byte. What comes with the copy, and what deliberately doesn’t:
  • The content, exactly — the copy renders identically to the source the moment it exists, so you start from a working page rather than a reconstruction of one.
  • Lead capture, pointed at itself — the lead forms on a page post to an event pipeline, and the pipeline it posts to is written into the page’s own code. A plain duplicate would keep the original’s pipeline, so every lead the copy captured would be filed against the page it was copied from, quietly, while both pages looked healthy. So Erdo duplicates the lead-form pipelines the page owns onto the copy and rewrites those references in the copied content. The copy captures its own leads, and the two pages’ numbers stay separate and true.
  • Not the publish state — the copy is private, whether or not the source is live. Publishing is a decision about a specific page, and copying a live page should never put a second public URL in front of visitors by accident. Publish the copy explicitly when it’s ready.
  • Analytics, automatically — a copy is an ordinary page, so the first time it’s visited publicly it gets session replay, heatmaps, and the first-party page-events stream described below, under your organization’s own project.
The copy is titled Copy of <source title> unless you name it. From a conversation you just ask (“make a copy of the Miami landing page called Miami — Spanish”). From the terminal:
The command prints the new page’s id and its editor URL, and reports each duplicated pipeline. On the REST API and MCP it’s the deploy surface’s clone mode: POST /v1/pages (the erdo_deploy_page tool) with {"clone_from_page_id": "...", "title": "..."} instead of content.
Building pages programmatically (deploying via the API, MCP, or SDK, and the window.erdo client) is covered in the developer guide, Build Apps.
A lead form that asks for a phone number also records whether the person agreed to be called or texted, and it asks in the way the number’s country requires:
  • A US or Canadian number (+1) sees an explicit checkbox, unchecked by default and never required. US rules need prior express written consent before marketing calls or texts, and a sentence under the submit button is not that.
  • Any other number sees a disclosure sentence, and submitting the form is the consent.
Either one appears once, directly above the button on the step that asks for the phone number, so a lead who stops after that step has already answered. Later steps do not repeat it. A page can ask for it on a later step instead. The form decides live from the country picked in the phone field, so a visitor who switches the flag from Colombia to the United States sees the checkbox before they submit. Both sentences cover calls, text messages and WhatsApp messages, name your business and Erdo as the sender, and carry the standard message-rate, frequency, and “Reply STOP” clauses. The wording is fixed and comes in the page’s language (English, Spanish, Portuguese, French); the page supplies only your business name. Each lead then carries three columns in your leads dataset. They appear on the dataset automatically — the page does not have to be wired for them, and a page built before consent existed records it as soon as it asks: A US visitor who gives their contact details on the first step and leaves before the second has no consent recorded, which is the honest reading of someone who never saw the box. If the same person comes back and ticks the box on a later visit, the yes replaces the earlier no; leaving the box unticked later never turns a yes back into a no, because someone who ignores the box has not withdrawn anything. To withdraw consent they reply STOP, which the consent ledger records. The email you get about a new lead says this in one line next to their phone number — “Consent to calls and texts: Yes — ticked the opt-in box” — rather than printing the legal sentence, which stays in the dataset as your record of what they agreed to. A lead with consent_given = true is evidence you may contact them; texting through Erdo still goes through the consent ledger that the sender checks before every message.

Session replay & heatmaps

Your published pages come with session replay and heatmaps automatically — see how real visitors actually behave: where they click, how far they scroll, where they drop off. There’s nothing to turn on. The first time a public page is visited, Erdo provisions an isolated analytics project for your organization (so your data is never mixed with another customer’s) and records real visitors from then on — this applies to pages you’ve already published, not just new ones. A few things are handled for you:
  • Form inputs are masked by default — names, emails and other typed values are hidden in replays, so you see behaviour without capturing personal data.
  • Your own previews don’t count — pages opened in preview/screenshot mode (and staff previews) are never recorded, so your numbers reflect real visitors only.
  • It’s per-organization — every published page in your workspace shares one project, and that project is yours alone.
Just ask to review them:
“How are visitors using my landing page — where are they dropping off?”

Querying your page analytics

Beyond individual session replays, Erdo can query your page-analytics events directly — every event a published page fires carries the page, the experiment variant (when one is running), the URL, and campaign parameters, so questions about real visitor behaviour are answerable with a query rather than a fixed report. Conversion rates per variant, form starts by campaign, drop-off between pageview and lead submission: Erdo writes the measurement to fit the question. When you’re running page variants against each other, ask:
“Which variant of my landing page converts better — show me form starts and lead submits per variant for the last two weeks.”
Queries are read-only and always scoped to your organization’s own analytics project. This is telemetry Erdo collects from your published pages, not data from a separate analytics-provider account you may have connected to Erdo. Ask explicitly about that connected provider when you want Erdo to inspect the provider account instead.

First-party page events

Your published pages also record their key conversion events directly into your own Erdo workspace — first-party, with no retention window and no vendor in the read path. Each published page posts a small, fixed set of named events to its own event pipeline, which writes them to a shared Page events dataset in your organization: Every row carries the page (artifact_id), the experiment variant the visitor was actually served, a per-visit session key, the page URL, and campaign attribution (utm_*, gclid, fbclid) — so per-variant funnels are a plain SQL query over your own dataset, and they line up with your leads because both streams stamp identical attribution. Ask Erdo:
“From the Page events dataset, show pageview → form start → lead conversion per variant for the last two weeks.”
For web_vital rows, the metric is also available in first-class columns: vital_name (LCP, INP, or CLS), vital_value, and vital_rating. INP is recorded only when the visitor interacts, and browsers that do not expose a metric’s performance API simply omit that metric rather than recording a zero. Replay-derived rows carry observation_source=session_replay, their replay session and source-event identifiers, and the observation timestamp. scroll_bail promotes scroll_depth_percent (and, when available, content_depth_percent); rage_click promotes a redaction-safe target_element; and form_abandon promotes only a conservative name or id as form_field. aria-label prose is never persisted. Identifiers must be short ASCII names, and UUID-, secret-, token-, credential-, and long compact opaque values are omitted. Credential markers are recognized across hyphen, underscore, and compact spellings (including api-key, private-key, restricted-key, and client-key). Useful names such as work_email, pricing-cta, checkout_step_2, and account_settings_form are preserved whole. Typed form values, DOM text, and raw replay data are never copied into this dataset. Replay-derived names are server-authored and cannot be forged through the page’s public event endpoint. Their deterministic producer key is enforced at the trusted write boundary. A receipt settles only after exactly one keyed write reaches the organization’s canonical Page events dataset; missing/non-write actions, a redirected target, or multiple writes fail closed. Retrying an interrupted or crash-after-write attempt updates the same row instead of losing or appending a duplicate. Replay-derived rows are rolling out — Erdo enables them workspace by workspace, so your Page events dataset may not carry them yet. Where they are present, Erdo also reads them back when it rebuilds a published page: how many recorded visits ended at a scroll depth, how often visitors clicked repeatedly at one element and which element that was, and how many of the people who started your form left without submitting. Where they are absent, the page builder is told those signals are unknown rather than clean — a page nobody’s visit was recorded on is never mistaken for a page nobody struggled with. You can ask for them directly too:
“From the Page events dataset, show me the rage_click targets and form_abandon fields on my pricing page over the last month.”
This is a curated stream of discrete events, not a session recorder — raw session replay, heatmaps, and click autocapture stay with the session-analytics project above. It’s on automatically for published pages (the first public view provisions the pipeline and dataset), preview and screenshot renders never emit, and events with names outside the fixed set are rejected at the pipeline. To turn it off for a page, disable that page’s page-events pipeline.

Google Analytics

Your published pages are also wired up to Google Analytics automatically — Erdo provisions an isolated GA4 property for your organization and tracks pageviews and events there, alongside session replay. Same as session replay: nothing to set up, no measurement ID to paste, no Google account of your own required — it’s handled for you and your data stays in your own property. GA4 is write-only from Erdo’s perspective: replay observations are read from PostHog and written to your Page events dataset, never read or imported from GA4.