deed.page defaults to permanent hosting. The first anonymous publish does not expire, and there is no claim window. That is the right default for reports, dashboards, and anything an agent wants to link later. Sometimes you still want the opposite: a preview that should disappear after a review window, a CI artifact that should not sit forever, or a debug dump that should not consume a Free plan slot after the day ends.
For those cases the API accepts an opt-in TTL. Omit it and the site stays permanent. Set it and the response includes expires_at; after that time the slug stops serving.
REST: X-Ttl or ?ttl=
TTL is a positive number of seconds. Pass it as the X-Ttl header or the ttl query parameter. Either form works with PUT or POST.
A one-hour HTML preview:
curl -sS -T preview.html \
"https://deed.page/v1/sites?slug=ci-preview-$(date +%Y%m%d-%H%M)" \
-H "X-Ttl: 3600"Or a JSON file map with a 24-hour window:
curl -sS -X POST "https://deed.page/v1/sites?ttl=86400" \
-H 'content-type: application/json' \
-d '{
"slug": "agent-scratch",
"files": {
"index.html": "<!doctype html><h1>scratch</h1><p>auto-expires</p>"
}
}'Typical success fields: ok, url (for example https://agent-scratch.deed.page/), path_url, version, permanent (false when TTL is set), expires_at (ISO-8601), bytes, files, and — on the first anonymous publish only — token. Store that deed_tok_… value; updates and deletes still need it even when the site is temporary.
Invalid values (zero, negative, or non-numeric) return 400 with code bad_request and the message that X-Ttl must be a positive number of seconds.
MCP: ttl_seconds on publish_site
The Streamable HTTP MCP server at https://deed.page/mcp exposes the same option as ttl_seconds on publish_site and update_site:
{
"name": "publish_site",
"arguments": {
"slug": "pr-42-preview",
"ttl_seconds": 7200,
"files": {
"index.html": "<!doctype html><h1>PR 42</h1>"
}
}
}Omit ttl_seconds for a permanent site. The tool response mirrors REST: permanent, expires_at, and the one-time token when you published anonymously.
What happens when it expires
After expires_at, GET on the public URL and get_site behave as if the slug is gone (404 / not_found). Listing with your Bearer token skips expired sites. You can delete early with DELETE /v1/sites/{slug} (or MCP delete_site) if you no longer need the URL.
A later publish to the same slug with your token can set a new TTL, clear expiry by omitting TTL (the site becomes permanent again), or replace the files. Treat TTL like any other publish field: send what you want the slug to be after this request.
Patterns that fit agents
- CI previews: Use a slug that includes the PR number or run id, set
X-Ttlto cover the review window (for example172800for 48 hours), and post the returned URL in the PR comment. No inbox or claim step. - Cron scratchpads: Nightly jobs can publish a status page with a TTL slightly longer than the schedule interval so stale runs disappear if the next job fails before overwrite.
- Debug dumps: Agents that materialize HTML traces can publish with a short TTL (minutes to a few hours) so the Free plan's site count is not filled with forgotten experiments.
- Keep the token either way: Temporary does not mean public write access. Strangers still get
409 slug_taken. Only the Bearer token updates or deletes the slug before expiry.
What TTL is not
- It is not the default. Permanent remains the product default; TTL is opt-in every time you want expiry.
- It is not a plan feature gate. Free, Hobby, and Pro can all set
X-Ttl/ttl_seconds. - It does not replace plan quotas. Expired sites stop serving, but you should still prefer unique slugs or deletes so your principal's site list stays tidy.
- It is not related to the ~4 MB per-request body cap. Large temporary sites still use
X-Merge: 1the same way permanent ones do.
Related
- Why permanent is the default: /blog/publish-without-a-human
- MCP walkthrough: /blog/mcp-publish-walkthrough
- Large sites with merge: /blog/merge-publish-large-sites
- Docs: /docs
- OpenAPI: /openapi.json
- Skill for agents: /skill.md
Questions or corrections: ops@avatar33.com.