Agents generate HTML constantly — reports, dashboards, prototypes, mockups — and then have nowhere to put it. Screenshots of markup are sad. ship.page is the missing last mile: one anonymous POST and the thing is on the public internet, on its own subdomain, with a link the agent can hand to its human.
One POST, one link
There is nothing to set up. No account, no API key, no project, no region picker. The agent streams HTML at the endpoint and reads url out of the JSON response:
deploy.sh
curl -X POST https://ship.page/deploy \ -H "Content-Type: text/html" \ --data-binary @report.html→ { "url": "https://vast-juice-c2dse08p.shipped.page/", … }
Multi-file sites go up as a JSON files map ({"files": {...}}, base64 for binaries) or as a zip of the whole build folder. Every drop mounts at the root of its own unguessable subdomain, so absolute paths just work, and the URL itself is the access control — viewers never log in. Anonymous drops live 7 days; ?ttl= shortens that.
Errors that talk to the human
The interesting part for agents isn't the happy path — it's what happens when they hit a wall. Every error is now a structured envelope: error and a machine-stable code, plus, when it helps, a hint, an upgrade_url and a docs_url.
The hint is written for the human, not the model. When an agent gets a 402 back from a subscriber-gated feature, the correct move is to relay the hint verbatim: sign in at ship.page, subscribe in the dashboard, mint an sp_ key, hand it back. The agent can't check out — but it can read the instructions aloud. Rate limits are equally explicit: a 429 always carries Retry-After: 60.
Parse code, not prose
error text can change; code is the contract. Switch on the code, relay the hint, respect the Retry-After. That's the whole error-handling strategy.
MCP, if you'd rather have tools
For agents that speak the Model Context Protocol, there's a stateless endpoint at POST https://ship.page/mcp (JSON-RPC 2.0, per the 2026-07-28 spec) exposing five tools: deploy_html, deploy_files, list_drops, delete_drop and get_limits. Auth is optional pass-through — an Authorization: Bearer sp_… header makes the tools act as that account; no header means anonymous deploys.
{
"mcpServers": {
"ship.page": { "url": "https://ship.page/mcp" }
}
}
The whole API is also published as an OpenAPI 3.1 spec at /openapi.json, and everything above — quickstart, limits, error contract, the upgrade hand-off — lives on one page written for machine readers: /docs/agents.
What a workflow looks like
A coding agent finishes a refactor and renders a before/after metrics report as HTML. It POSTs the file, gets https://warm-bison-x3k91pq2.shipped.page/, and drops the link in its reply. Thirty seconds of work, zero credentials, and the human is looking at a real page instead of a wall of markdown in a terminal.
Two weeks later the same agent tries ?name=daily-report and gets a 402. The body tells it exactly what to say: "this needs a paid plan — Pro is $4/mo, sign in, subscribe, mint a key, give it to me." The user does the two-minute human part, the agent retries with the key in the Authorization header, and the named drop goes through.
That's the shape we wanted: the machine-readable path is the default, and the moment a human decision is required — money, an account — the API says so in plain language the agent can pass along untouched.