---
title: "Put a page that was deleted or expired back at the same URL."
method: POST
path: "/sites/{idOrSlug}/restore"
tags: ["Sites"]
---

# Put a page that was deleted or expired back at the same URL.

`POST /sites/{idOrSlug}/restore`

## Request

- Base URL: `https://api.stacktr.ee`
- URL: `https://api.stacktr.ee/sites/{idOrSlug}/restore`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.stacktr.ee/oauth/authorize`, token URL `https://api.stacktr.ee/oauth/token`, scopes: `sites:write`)

The undo half of DELETE. Same id, same unlisted token, same slug, same read history, so every link already sent starts working again. This, not a fresh publish, is the answer to a 409 site_deleted. Republishing would mint a different URL, strand everyone holding the old one, and spend another lifetime page; a restore spends none. It is a RESCUE, NOT A RENEWAL: a page that ran out of time on a plan with an expiry ceiling comes back for 48 hours rather than a fresh full lifetime, so read `expires_at_iso` and `restored_for` off the response and tell the user that date. Owner-authed, and only for a page this account owns. Every failure is the same 404 body on purpose (the endpoint accepts a token, so a distinguishable answer would be an oracle over other people's pages): past the 30 days, never retired, or taken down for abuse, which is never restorable.

## Response `200`

Serving again at the same URL.

- object
  - `ok` boolean, required
  - `id` string
  - `url` string, uri: The same link the page had before; nothing new needs sending.
  - `expires_at` integer, nullable: Unix epoch seconds the restored page now dies at, or null for never. Authoritative: do not assume a fresh full window.
  - `restored_for` 'grace' | 'kept' | 'plan': `grace` = the 48-hour rescue window, because the page had run out of time on a plan with an expiry ceiling; `kept` = it came back on the deadline it already had, including no deadline at all, which is what a paid permanent page keeps; `plan` = a fresh normal window for this plan.
  - `restore_grace_hours` integer: How long a rescued page comes back for when `restored_for` is `grace`. 48 hours. Present on every restore so a client can state the policy without hardcoding it.
  - `expires_at_iso` string, nullable: The restored deadline as RFC 3339 UTC. On a 48-hour rescue this exact moment is the message.
  - `ttl_seconds` integer, nullable: Seconds from now until it stops serving again. Null for never.
  - `message` string: The same thing in words, safe to repeat to the user.

## Other responses

- `401`: Missing or invalid credentials.
- `402`: A plan ceiling was hit. The feature or the quota is not on this plan; the caller needs a different plan or an x402 unlock.
- `403`: Account suspended. A restore puts content back on the public web, so it meets the same blocklist a publish does.
- `404`: Nothing restorable here: past the 30-day window, not retired, not yours, or an abuse takedown. One body for all of them, deliberately.

---

[API](https://skmtc.dev/stacktr/apis/stacktree-api.md) · [All operations](https://skmtc.dev/stacktr/apis/stacktree-api/llms.txt) · [OpenAPI document](https://skmtc.dev/stacktr/apis/stacktree-api/revisions/5878cce46977?raw)
