Secure AI Artifact Distribution That Holds Up

HTMLvault Team·May 31, 2026·15 min read

Your AI tool has finished the work: a quarterly report, a pricing page, a pipeline dashboard. It renders and the numbers are right, but it has no address yet. Where it lives, who can open it, how long it stays open and who read it are still undecided. Settling those four things is secure AI artifact distribution.

This post is for the person whose AI tool (Claude, ChatGPT, Gemini, Cursor or an agent) produces reports and pages, and who decides where they go. The mechanics of sending any HTML file are in how to share HTML securely at work. This one covers what changes when an AI tool made the page and can publish it too.

The handoff: from finished AI output to a link someone opens

Secure AI artifact distribution is the step between a finished AI output and the person meant to read it. Four questions decide whether that step was governed or merely fast: what was in the page, who could open it, how long it stayed open, and where that is written down.

Start by mapping how output leaves your team now. Most of it travels four ways: attached to an email, pasted into a chat thread, copied into a shared doc, or uploaded to whatever host was quickest that afternoon. Each route answers one or two of the four questions. None answers all of them, and none tells you whether the page was read.

AI output strains those routes in three ways. There is more of it, so reviewing each page by hand is the first habit dropped under a deadline. It carries context nobody re-read, because the model wrote from whatever it was given. And the tool can publish on its own, so the handoff can happen mid-conversation with no human step unless you design one.

That last change is also the fix. If the destination answers the four questions and the AI tool can reach it directly, the governed route becomes the fast one. In practice, that is the difference between governing AI-generated deliverables and just producing more of them.

Generate anywhere, host where you own it

Most AI tools can give you a URL for a page they generated, and the convenience is real. But generating and hosting are different jobs with different owners, and a chat-hosted URL gives the hosting job to the vendor by default.

A page deployed from a chat platform lives on that vendor's infrastructure and domain, inside their account model. Their plan and workspace settings decide who can open it, how long it lasts and what record exists. Some tools offer no static export at all, so what gets deployed is a live build in their environment rather than a file you can move. For a workspace-private internal tool, that is often fine. Once the page goes to a customer, three things change:

  • Branding and reputation. A proposal on a vendor's host puts the vendor's name in the address bar and builds that host's reputation, not yours. White-label domains put the link on your own name and your own sending reputation. Pro and Teams include one, Enterprise includes three, and a branded subdomain such as you.hvlt.io is the lighter option.
  • The record. When someone asks who opened the page and when access ended, the answer should come from a system your organization controls, not a support ticket and a screenshot of a chat. The enterprise sharing checklist covers what that record should hold.
  • Portability. You can swap one model for another, and you shouldn't have to pick a new host each time you do. An HTMLvault page is your own HTML, served as written: it carries its own CSS and fonts, its scripts run, and HTMLvault adds no stylesheet of its own. A report Claude wrote last quarter and one Gemini writes next week publish the same way, and leaving means taking the file with you.

HTMLvault doesn't write the HTML. It gives the finished page an address you own, checks it before it goes live and measures it after it lands. It is also the fallback when a chat platform's own storage misbehaves and an artifact will not save.

Chat-hosted URL compared with a link on your own domain Five questions about a shared AI-generated page, answered for a URL hosted by the chat vendor and for an HTMLvault link: whose name is in the address bar, who sets access and expiry, where the read record lives, what happens when you switch AI tools, and whether the page is checked before it goes live. Chat-hosted URL versus a link on your domain QUESTION CHAT-HOSTED URL HTMLVAULT LINK Whose name is in the address bar Who sets access and expiry Where the read record lives When you switch AI tools Checked before it goes live THE VENDOR'S THEIR PLAN THEIR ACCOUNT OFTEN TIED TO IT VARIES BY TOOL YOUR DOMAIN YOU, PER LINK YOUR DASHBOARD PORTABLE HTML EVERY PUBLISH
A chat-hosted URL is fine for a workspace-private tool, but once an AI-generated report goes to a customer, the domain, the access settings and the read record should all belong to you.
Chip Bellfort let the chat tool publish his pricing page and had the link in the prospect's inbox before the call ended. The prospect's procurement lead opened the next meeting by asking how long Synergetics had been reselling the AI company whose name was in the address bar. Chip spent twenty minutes explaining what Synergetics actually sells, which is further than most people in the building have ever gotten.

Publish from the tool that made it

You can copy HTML out of a chat window and paste it into an upload form, but that's the step people skip when the deadline arrives. Let the tool that wrote the page publish it.

Connect the MCP server to Claude, Gemini or Cursor (ChatGPT connects through a Custom GPT action schema), and the tool can call HTMLvault directly. scan_html shows what the PII and secret checks would find, without publishing anything. create_link publishes the page and sets its expiry. create_recipient_links gives each reader their own tracked URL to that page, so the analytics later say who read it, not just that someone did. When each reader needs their own version rather than their own link, create_links publishes up to 100 pages from one template in a single call, filling {{name}}-style placeholders from a vars map per row. A placeholder with no matching var passes through untouched; a var with no matching placeholder fails its row.

A single document can be up to 5 MB. For a long report written in sections, the model can start a draft with create_draft_link, send chunks of up to 200 KB with append_html and finalize on the last call. Nothing is public and no checks run until it is finalized.

Paste this prompt into Claude's project instructions or your agent's system prompt, and every report goes out the same way:

When a report is final, publish it to HTMLvault:
1. Run scan_html on the complete HTML. If it reports any
   findings, list them for me and stop. Do not publish.
2. If the scan is clean, call create_link with the HTML and
   expiry set to 7 days.
3. Call create_recipient_links on that link for: [names].
4. Reply with one line per recipient: name, then URL.
Because one prompt runs the scan, the publish and the per-reader links, the governed route takes no more effort than pasting a chat-hosted URL into an email.

The same sequence runs without a chat window. Zapier, Clay or a script calls the REST API with an API key: POST /api/v1/links for one page, /api/v1/links/batch for up to 100 from a template, /api/v1/links/draft for append mode. There is no REST scan endpoint. The publish call does the checking: a refused publish returns the findings instead of a URL, so review happens in your script's error branch. If your pipeline produces a report fragment with charts, or a Word file, POST /api/v1/links/document builds a themed, self-contained page from it.

See what happened after you sent it

Per-link analytics record views, unique and repeat viewers, scroll depth, time on page, country and city, device and browser, and referrer. With one link per recipient, those numbers belong to a person: the finance lead read to the end, the sponsor stopped at the summary. Channel links (Pro and above) give one page a separate URL per channel with server-side UTM attribution. If the page should report into your own stack, add your GA4, Meta or HubSpot code.

The agent can read the numbers too. get_analytics returns them over MCP, so the tool that wrote and published a report can tell you a week later who opened it and how far they got. Webhooks send each view as it happens to Zapier, Clay or your own code, and from there to the CRM, so a recipient.viewed event can open the follow-up task.

Treat all of it as context, not a verdict. Three repeat visits from a second city and heavy scrolling on the pricing section usually means procurement, not your champion. Weigh it against what the account owner already knows. Scroll depth tells you where attention stopped, not why. And a report nobody opened may just have been unnecessary, because the decision was made in the meeting.

Once the page was back on Synergetics' own domain, one link per person, Chip watched the analytics like a stock ticker. Nine minutes on pricing and three repeat visits from a city his champion has never lived in, so he moved the deal to Commit before lunch. It was the procurement lead from the reseller meeting, itemizing every line Chip could be talked down on, and the deal did close, at the number at the bottom of her list.

What generated markup carries that the page does not show

A model writes from the context it was given. If the prompt held a CRM export, a log snippet or a credential, the output can repeat it somewhere the rendered page doesn't show: a key in a fetch() header, contact rows in an inline JSON blob or a data- attribute, an internal filename in a meta tag, a signed storage URL on an image. In reports, the usual leak is the collapsed tab below the charts: a "raw data" section the model added, which reviewers scrolled past because it was closed.

Publishing drops HTML comments, so a note the model left in a comment is never served. Everything else in the markup is. Every publish and edit is checked against your account's PII and secret policy (regex, zero tokens), and every outside URL against Google's unsafe-URL lists. A refused publish returns the findings. On Teams and Enterprise, Publishing Rules decide which findings block. The guide to scanning the HTML for API keys covers what the checks catch and how to fix each finding.

Where secrets hide in AI-generated HTML and what catches each Four places a model can leave sensitive values in a generated page: the regex scan flags a bearer token in a fetch header and contact rows in inline JSON, publishing drops HTML comments, and margin figures in a collapsed tab need human review. Where secrets hide in generated markup LOCATION IN THE PAGE WHAT CATCHES IT fetch() header: a bearer token Inline JSON or data- attribute: contact rows HTML comment: a leftover prompt note Collapsed tab below the charts: margin by account REGEX SCAN FLAGS REGEX SCAN FLAGS DROPPED AT PUBLISH HUMAN REVIEW
Publishing removes HTML comments and the regex check flags pattern-shaped values like keys and emails, but figures that are only sensitive in context, such as margins in a collapsed tab, still need a person to open the source.

When a finding is real, redact with context and keep what the reader needs. Show that six contacts share one company domain without printing six addresses. Show a credential's shape (Authorization: Bearer [redacted, 40 chars]) without its value. Rotate any key that appeared in a page that left your team, and rebuild whatever depended on it: a key in client-side code was never safe, only unnoticed.

Defaults for AI deliverables: expiry, password, retention, never indexed

There is no org-wide expiry default, so the default is whatever your prompt says. Put an expiry in the instructions your AI tool follows and every link it creates gets one. Over MCP, create_link accepts 1 hour, 24 hours, 3 days, 7 days, 30 days or never. On the web, Pro and above can also choose 1 year. Free links expire after 30 days. Match the window to the job: 24 hours for a daily status page, 7 days for a proposal under review, never for a reference page you keep current. Configurable expiry can be changed or extended after you send, so a short default costs little.

Revocation matters as much as expiry, because the reason to close a link rarely arrives on schedule. If the recipient list changes, a credential turns up in a sent page or the engagement ends, delete the link with delete_link or from the dashboard. Don't wait for the date.

Passwords are set on the web. On Pro and above you add one per link in the upload form or the links dashboard; MCP and the REST API cannot set one. For a sensitive report, let the agent publish, add the password before you send the URL, and send the password through a different channel. The password is checked on the server. A correct entry unlocks the page in that browser for up to a day, changing the password ends every earlier unlock, and a reader who doesn't have it can press "Ask for password" to email you. The details are in password-protected links.

Retention is one setting for the whole account; for an org member, the org owner's setting applies. It runs 7 days to 2 years on paid plans and 7 to 90 days on Free, counted from each link's expiry. After that, the content and per-visitor view data are deleted. There is no per-link retention and no way to set it through MCP or the API, so choose a data-retention window that fits the most sensitive thing your agents publish.

Never indexed needs no setting: every link is served noindex, with no robots file for anyone to forget. For pages hosted elsewhere, how AI crawlers are blocked is a separate topic. Together, these defaults give IT a short answer when it asks whether this is a tool defensible in procurement.

Test the pipeline with seeded examples

Before a team relies on the pipeline, feed it four examples built to fail in known ways. They show you where the controls are too weak, and where they're so heavy that people will work around them. Preview each one with scan_html, then publish it for real.

  1. A fake token. An invented, never-issued API key in a fetch() header. The scan should flag it, and the publish should be refused with the finding. If it goes live, the policy is too loose for credentials. Never seed a real key, even a revoked one.
  2. An email list. Made-up customer addresses in a collapsed tab below the charts. The scan should find every address. Then decide what ought to happen: in a client deliverable the addresses go, while an internal review might only need a password. On Teams and Enterprise, Publishing Rules can treat public and password-protected pages differently, and adding or removing a password re-runs them.
  3. An expired project. Publish a page with a 1-hour expiry and open it after the hour. Delete a live link and confirm the URL stops working. Check the account's retention window against what your policy says.
  4. A harmless summary. A clean one-page summary should go from prompt to link in one step. If it gets refused or needs three approvals, the pipeline is too heavy.

Rerun the set whenever the prompt, the rules or the tool changes. Security that people cannot use is merely a policy with better typography.

Honest trade-offs

  • Documents, not stateful apps. HTMLvault hosts HTML pages that you distribute and measure. Scripts run, so a Chart.js dashboard works. A page that needs server-side sessions, per-user stored state or a database behind it needs a different kind of product.
  • A regex scan is a net. It catches structured patterns (keys, emails, phone numbers, account numbers) at zero token cost. It won't catch an unreleased roadmap date, a discount you didn't mean to show, or margins in a collapsed tab. Teams and Enterprise can add a BYOK AI scan with their own Anthropic, OpenAI or Google key, which runs after the link is published. A person opening the source once still catches what both miss. See where regex and AI scanning each fail.
  • Noindex is not "no forwarding." Keeping a page out of search closes one path. Anyone with the URL can open an unprotected link, and a forward only shows up as an unfamiliar city on someone's link. Passwords, short expiries, per-recipient links and deleting finished links close the rest.
  • Automation stops at the password. MCP and the API cannot set one, so a fully automated pipeline publishes unprotected links. Send sensitive deliverables through the dashboard.
  • Free is trial-shaped. 50 links a month and a 30-day expiry. Pro ($12/mo, $10/mo annual) adds unlimited links, configurable expiry, channel links and up to five webhooks.

Put the prompt from the publishing section into your project instructions and set the retention window once. From then on, every report your AI tool finishes goes out scanned, on your domain, set to expire in seven days, with a separate link for each reader. A week later, you can ask the same tool who read it.

ai-generated-htmlmcphtml-sharinglink-analyticssecret-scanninglink-expiry
HTMLvault

Publish your AI creations. See who reads them.

Proposals, dashboards and reports from Claude, ChatGPT or any AI tool become private links on your own domain. Update them after you send, and see who read what, down to the scroll.

Create a link

Related posts