<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: deed.page</title>
    <description>The latest articles on DEV Community by deed.page (@deedpage).</description>
    <link>https://dev.to/deedpage</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215993%2F47e5dc6d-f5f8-44f5-8ca1-deacf0177b7e.png</url>
      <title>DEV Community: deed.page</title>
      <link>https://dev.to/deedpage</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deedpage"/>
    <language>en</language>
    <item>
      <title>Publish an AI agent's HTML report to a live URL with one curl call</title>
      <dc:creator>deed.page</dc:creator>
      <pubDate>Thu, 29 Oct 2026 19:56:38 +0000</pubDate>
      <link>https://dev.to/deedpage/publish-an-ai-agents-html-report-to-a-live-url-with-one-curl-call-38p9</link>
      <guid>https://dev.to/deedpage/publish-an-ai-agents-html-report-to-a-live-url-with-one-curl-call-38p9</guid>
      <description>&lt;p&gt;Agents are good at producing HTML: test summaries, research briefs, dashboards, small prototypes. Getting that file onto a URL someone can open usually takes longer than writing it. You have to pick a host, sign up, set up a CLI or connect a repo, then wait for a build.&lt;/p&gt;

&lt;p&gt;This walkthrough uses &lt;a href="https://deed.page" rel="noopener noreferrer"&gt;deed.page&lt;/a&gt;, a static host built for this case. You can publish without an account, the response is JSON an agent can parse, and updates are versioned, so a script can safely overwrite its own report every night.&lt;/p&gt;

&lt;p&gt;Everything below is plain &lt;code&gt;curl&lt;/code&gt;. You can run it yourself or give it to an agent as a shell tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Write the report
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; report.html &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="sh"&gt;'
&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset="utf-8"&amp;gt;&amp;lt;title&amp;gt;Nightly test report&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Nightly test report&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;412 passed, 3 failed.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;HTML
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Publish it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SLUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;agent-report-&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 3&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; report.html &lt;span class="s2"&gt;"https://deed.page/v1/sites?slug=&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Client: report-bot"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; first.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;curl -T&lt;/code&gt; sends a &lt;code&gt;PUT&lt;/code&gt; with the file as the body. No API key is needed. The response looks like this (trimmed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://agent-report-87cc65.deed.page/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://v1--agent-report-87cc65.deed.page/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deed_tok_â€¦"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site is live right away, and &lt;code&gt;version_url&lt;/code&gt; is a permanent link to this exact version.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;token&lt;/code&gt; is only shown once, and it's what lets you update or delete the site later. Save it to a file outside the agent's context instead of printing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/deed-page &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.config/deed-page
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json;print(json.load(open('first.json'))['token'])"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.config/deed-page/token
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.config/deed-page/token &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;first.json
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.config/deed-page/token&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an LLM is driving the shell, this matters. A token that shows up in tool output ends up in the transcript, the logs and possibly the next prompt. Keep it in a file and let the tool read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Update it safely
&lt;/h2&gt;

&lt;p&gt;The next night the numbers change. Publish to the same slug with the token. Add &lt;code&gt;If-Match&lt;/code&gt; so the write only succeeds if the site is still at the version you expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/412 passed, 3 failed/415 passed, 0 failed/'&lt;/span&gt; report.html
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; report.html &lt;span class="s2"&gt;"https://deed.page/v1/sites?slug=&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'If-Match: "v1"'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; "version": "v2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If two agents (or two runs of the same cron job) race, the one holding a stale version gets a &lt;code&gt;409&lt;/code&gt; instead of silently overwriting the other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"version_conflict"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Slug agent-report-87cc65 is at v2, not v1. Re-read and retry."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"v2"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error body tells the agent the current version, so it can re-read the site and retry. If you publish exactly the same bytes again, you get &lt;code&gt;"unchanged": true&lt;/code&gt; and no new version.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Look back and roll back
&lt;/h2&gt;

&lt;p&gt;Every publish is kept as a version (the free plan keeps the last 10):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://deed.page/v1/sites/&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;/versions"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry has a &lt;code&gt;permalink&lt;/code&gt; like &lt;code&gt;https://v2--agent-report-87cc65.deed.page/&lt;/code&gt;. If tonight's report is wrong, restore an older one. The restore becomes a new version, so history is never rewritten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://deed.page/v1/sites/&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;/versions/v1/restore"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; "version": "v3", content equal to v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Clean up
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE &lt;span class="s2"&gt;"https://deed.page/v1/sites/&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"ok":true,"deleted":"agent-report-87cc65"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The URL returns 404 a few seconds later.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than one file
&lt;/h2&gt;

&lt;p&gt;For a folder (HTML plus CSS, images and JS), pack it and upload the tarball the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; ./dist &lt;span class="nt"&gt;-czf&lt;/span&gt; site.tar.gz &lt;span class="nb"&gt;.&lt;/span&gt;
curl &lt;span class="nt"&gt;-T&lt;/span&gt; site.tar.gz &lt;span class="s2"&gt;"https://deed.page/v1/sites?slug=&lt;/span&gt;&lt;span class="nv"&gt;$SLUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEDPAGE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean URLs work (&lt;code&gt;/about&lt;/code&gt; serves &lt;code&gt;about.html&lt;/code&gt; or &lt;code&gt;about/index.html&lt;/code&gt;), and a root &lt;code&gt;404.html&lt;/code&gt; is used for missing pages. Single-page apps can send &lt;code&gt;X-Spa: 1&lt;/code&gt; so unknown paths fall back to &lt;code&gt;index.html&lt;/code&gt;. A single request is capped at 4 MB, and larger sites use the manifest upload described in the &lt;a href="https://deed.page/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Letting an agent do this
&lt;/h2&gt;

&lt;p&gt;If your agent runs shell commands, the steps above are all it needs. Two guardrails are worth adding:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ask before publishing.&lt;/strong&gt; A URL is public. Have the agent show what it is about to publish (file list and a preview) and wait for an explicit "yes" before running the &lt;code&gt;PUT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the token out of the conversation.&lt;/strong&gt; Read it from &lt;code&gt;~/.config/deed-page/token&lt;/code&gt; inside the tool, never echo it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For agents that speak MCP, deed.page also has an MCP endpoint at &lt;code&gt;https://deed.page/mcp&lt;/code&gt;. There's an open-source agent skill that follows both rules at &lt;a href="https://github.com/f-o-x11/deed-page-skill" rel="noopener noreferrer"&gt;github.com/f-o-x11/deed-page-skill&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits worth knowing
&lt;/h2&gt;

&lt;p&gt;The free plan allows 25 sites, 25 MB per site, 60 publishes an hour and 10 versions per site. Free sites show a small "Published with deed.page" badge. The paid plans are listed on &lt;a href="https://deed.page" rel="noopener noreferrer"&gt;deed.page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Full API reference: &lt;a href="https://deed.page/docs" rel="noopener noreferrer"&gt;deed.page/docs&lt;/a&gt;. There's also a plain-text version for LLMs at &lt;a href="https://deed.page/llms.txt" rel="noopener noreferrer"&gt;deed.page/llms.txt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>webdev</category>
      <category>curl</category>
    </item>
  </channel>
</rss>
