Skip to content

retention_expired

HTTP 410. The render you asked for exists, and it is returned in full in details.render — but its file has passed its retention period, so no URL is signed for it. The file is deleted and cannot be restored.

Only GET /v1/renders/:id (and the MCP get_render tool) answers this.

Every render is stamped with an expires_at when it is made, from the account’s plan at that moment: 7 days on Free and the keyless trial, 30 days on Solo and Starter, 90 days on Growth and Scale. That deadline never moves afterwards — changing plan later does not shorten or lengthen the retention of renders already made.

Until expires_at, GET /v1/renders/:id re-signs a URL to the file as often as you like, for free. From the moment it passes:

  • GET /v1/renders/:id answers 410 retention_expired instead of signing a URL;
  • GET /v1/renders still lists the render, with url: null;
  • the render is no longer a cache hit, so an identical POST /v1/render renders again;
  • a daily sweep deletes the stored file, normally within a day, and records the time in file_deleted_at. Until the sweep reaches it, file_deleted_at is null — but the file is already no longer served;
  • the same sweep deletes the data and options the render was made from. A failed render, which never had a file, loses its payload at its own expires_at too, and its error keeps only its type.

The render record is kept: its id, template version, status, format, page count, size and cost stay on your account. Nothing from the payload does, so we can’t send it back to you.

410 — a Free render, 7 days after it was made
{
"error": {
"type": "retention_expired",
"message": "The file for render `rnd_7hq2m4x8k1bv` passed its retention on 2026-09-28T14:07:02.551Z (7 days, this account's plan when it was made) and has been deleted, with the data and options it was made from. The render record is kept. To have the file again, render it again: the same template version, data and options make the same document, billed as a new render.",
"docs_url": "https://galleyrender.com/docs/errors/retention_expired",
"details": {
"render": {
"object": "render",
"id": "rnd_7hq2m4x8k1bv",
"status": "succeeded",
"template": "invoice@1",
"format": "pdf",
"engine": "chromium",
"cached": false,
"url": null,
"expires_at": "2026-09-28T14:07:02.551Z",
"page_count": 1,
"billable_units": 1,
"byte_size": 27578,
"content_type": "application/pdf",
"created_at": "2026-09-21T14:07:02.551Z",
"completed_at": "2026-09-21T14:07:04.112Z",
"error": null,
"webhook_status": null,
"webhook_attempts": 0,
"file_deleted_at": "2026-09-29T09:12:40.018Z"
},
"expired_at": "2026-09-28T14:07:02.551Z",
"file_deleted_at": "2026-09-29T09:12:40.018Z",
"retention_days": 7,
"next_step": "POST /v1/render with template \"invoice@1\", format \"pdf\" and the same data and options as the original request. It is a new, billed render; nothing about this one can be restored.",
"rerender": {
"method": "POST",
"path": "/v1/render",
"template": "invoice@1",
"format": "pdf"
}
}
}
}

Do not retry the same request. The file will not come back.

  1. Render it again, if you still need the document. Send POST /v1/render with details.rerender.template — the pinned name@version, so the template is exactly the one used — the same format, and the same data and options you sent the first time, from your own records (ours were deleted with the file). Renders are deterministic, so it is the same document. It is billed as a new render.
  2. Keep your own copy next time. If a document has to outlive your plan’s retention, download it while the URL works and store it yourself. A plan with longer retention keeps files longer, but only for renders made after the change.