How the 30-day retention works

Saved projects are kept for 30 days from the day they are saved. That promise appears on the features page, the about page and in the editor; this guide explains the mechanism behind it exactly as the methodology page states it — an expiry timestamp written at save time, a database policy that makes expired rows invisible to every client, a background purge that removes them, and a downloaded file that none of this touches. Every figure below is read from the studio's engines when this page was built, on fictional inputs.

What a saved project is

"Save to my studio" writes one database row, and the retention window applies to that row. The editor's state module describes exactly which columns the row occupies. Run on a fictional project — a celebration with a confetti frame, a link, a caption, and photo-woven modules switched on — it produces 6 fields, title, occasion, qr_type, payload, style, frame, to which the store adds the composed design and the owner's identity:

{
  "title": "Garden party — place cards",
  "occasion": "Celebration",
  "qr_type": "link",
  "payload": {
    "type": "link",
    "value": "https://example.com/garden-party/album"
  },
  "style": {
    "moduleStyle": "classic",
    "photoWoven": true
  },
  "frame": {
    "template": "confetti",
    "palette": {
      "primary": "#be123c",
      "secondary": "#2563eb",
      "accent": "#f59e0b"
    },
    "caption": "Scan for the photos"
  }
}

Read the record closely. photoWoven is a boolean, not a picture; these state columns carry no picture data. The photo-derived artwork lives only in the composed design the store adds alongside them, and the working photo itself is never uploaded. The state module's rule is that the photo in photo-woven mode is deliberately not part of the state — photos are processed on-device only and never serialized — and a reopened photo-woven project shows its saved artwork and asks for the photo again before re-editing. The methodology page's sentence, "the saved record contains the project's settings and the composed design — the working photos themselves are not uploaded", is a description of this shape; the composition guide covers what else stays on your device.

The same module reads rows back. Rebuilding a state from the columns above returns the fictional project with every field identical. Parsing is total: a row missing its payload column reads as null, an occasion the studio does not compose for reads as null, and the editor starts fresh instead of wedging. The stash that carries a signed-out design through the signup round trip is versioned at 1: a serialized copy of the fictional project reparses identically, a copy stamped with a future version reads as null, and text that is not JSON reads as null.

The expiry timestamp

At save time the row receives an expiry timestamp set 30 days ahead. It is a value stored in the row, not a calculation the interface performs later: every row in My projects shows its own "kept until" date, computed from the database record itself, so the interface cannot show a date the record does not hold. Because the timestamp belongs to the row, duplicating a project — which writes a new row — gives the copy its own 30 days, and deleting a project, or everything at once from the account page, is simply your choice to end the window early.

The policy, not a filter and not a job

The methodology page's central sentence is this one: "the database's row-level security policy makes expired rows invisible to every client — the policy that grants you access to your own projects also requires them to be unexpired." Read it as one policy with a further condition: the row must belong to the requesting account and must be unexpired. The methodology page draws the consequence in its "Reading an expiry" section — "a client cannot show an expired project by ignoring the date — it never receives the row" — and among its limits: an expired row "is not returned to any client — no client setting or ignored date can fetch it". There is no "expired" state that a client can observe; there is only presence before the timestamp and absence after it.

The page insists on what expiry is not, and the distinction is mechanical. A scheduled cleanup job removes rows when it runs, so between runs an expired row would remain readable — the lag the methodology page names. A client-side filter runs in the browser, so a client that did not apply it would still receive the row — the bypass it names. A condition in the access policy has neither gap: "at the moment a project expires it stops being readable".

The purge

Invisibility is the enforcement; removal is housekeeping. The methodology page states that "a background purge permanently removes expired rows". The methodology page gives no schedule for the purge, and none is needed: the policy already hides the row the instant it expires. The order matters for reading the rest of this guide: expiry makes a row unreadable, then the purge deletes it. The purge is not what makes a project expire.

Reading a "kept until" date

Each row in My projects carries its own date, so two projects saved on different days show different dates and a duplicate shows a new one. The date is the record's expiry read back, not a reminder of when you saved: it is the day on which the row's expiry instant falls. Past that instant, the project is not marked as expired — it is absent from the list, because the policy returns nothing for it. If a design matters, download it before that date; the file is independent, as the next section shows.

Live Keepsakes carry their own expiry instant

A live keepsake is the one part of the studio that is deliberately not a downloadable file: the message is hosted in the database and each live keepsake ends 30 days after it is created. The mechanism is visible in what its code encodes. Where a static keepsake's symbol carries the destination itself, a live keepsake's symbol carries a pointer into the database: for a fictional id the engine's URL contract produces https://keepsakeqrcodes.com/r/?k=0f3c2a1e-6b7d-4c8e-9a0b-1d2e3f4a5b6c — the recipient path /r/ with the id in the query string, from which the recipient page recovers 0f3c2a1e-6b7d-4c8e-9a0b-1d2e3f4a5b6c and asks for the row. The recipient screen is a state machine, and this is how it answers:

Stage Event Next stage Meaning
loading loaded revealing The row was readable.
loading missing gone The reveal does not exist or has expired — the machine's own words.
loading failed error A network or parse failure, which can be retried.
gone loaded gone A later successful load cannot revive a gone reveal.

The machine documents its missing event as "the reveal does not exist or has expired", and it lands on gone. The methodology page states the rule from the owner's side: a live keepsake "carries its own expiry instant on its record; after that instant the reveal link goes dark, and a recipient who arrives at it is handled by the recipient state machine" as a missing reveal. The machine does not distinguish an expired reveal from one that never existed — both are the same event. That stage is terminal: a gone reveal stays gone whatever arrives afterwards. On the owner's side the record carries the instant explicitly: the parser reads the fictional row's expiry as 2026-11-02T18:30:00.000Z, and a row with no expiry at all is rejected as malformed — the expiry is part of the record's contract, not an optional extra. While the row is readable, the anonymous read path returns only occasion, message, style: applied to a fictional row that also carries id, user_id, title, expires_at, the allowlist parse returns 3 fields and structurally cannot return the others. The Live Keepsakes guide walks both machines in full.

Downloads are independent

The methodology page states "Download anything you want to keep; the downloaded file is independent of all of this", and the reason is in the file. A downloaded PNG or SVG contains a QR symbol whose modules encode the destination itself, placed exactly as entered. Encoding the fictional project's link produces a version 5 symbol of 37×37 modules — 45×45 with the 4-module quiet zone — at error correction H, and the text the symbol carries is byte-for-byte the link that was entered. Nothing in it refers to an account, a project id, an expiry, or this site; any camera app that reads QR codes reads it. For the PNG presets the engine's print-size readout at 300 DPI is:

PNG preset Print size
512 px 1.71 in × 1.71 in (43.3 mm × 43.3 mm) at 300 DPI
1024 px 3.41 in × 3.41 in (86.7 mm × 86.7 mm) at 300 DPI
2048 px 6.83 in × 6.83 in (173.4 mm × 173.4 mm) at 300 DPI

Those files are independent of this site and of your account. What a download does not protect is the destination it points to — the studio verifies the format of what you encode, not the future of the service it opens; the error-correction guide explains the symbol, and the limits guide explains why the methodology page points the longest-lived keepsakes at destinations you control.

Common misreadings

  • "After 30 days my printed code stops working." Retention applies to the saved row. The printed code encodes the destination itself and never consults the row.
  • "Expired projects are just hidden." They are invisible by database policy to every client, then permanently removed by the purge. There is no hidden list.
  • "Saving uploads my photos." The columns above plus the composed design are what saving writes; the working photo is not among them. photoWoven records that the mode was on, nothing more.
  • "The purge is what makes a project expire." The policy does, at the timestamp. The purge removes what the policy already hides.
  • "I can download a live keepsake so it outlives its 30 days." A live keepsake is deliberately not a downloadable file; its code points at the hosted row, the reveal link goes dark at the record's expiry instant, and a recipient who arrives afterwards is handled as a missing reveal, landing on gone.

Retention FAQ

When does the 30-day window start?

At save time. The saved row receives an expiry timestamp set 30 days ahead when it is written, and the "kept until" date shown in My projects is computed from that record. Duplicating a project writes a new row with its own timestamp, so the copy gets its own 30 days.

What happens to a project at the moment it expires?

It stops being readable. The database policy that grants you access to your own projects also requires them to be unexpired, so from that instant the row is not returned to any client. A background purge then permanently removes expired rows. Nothing you downloaded is affected.

Does an expired project show as "expired" in My projects?

No. Invisible means absent: the policy returns nothing for the row, so it does not appear in the list at all, labeled or otherwise. Download anything you want to keep before its "kept until" date passes.

Does retention affect the PNG or SVG I downloaded?

No. A downloaded file carries a QR symbol whose modules encode the destination itself — the fictional link on this page encodes as a version 5 symbol at error correction H with no reference to an account or a project. The file is independent of this site and of your account.

Do Live Keepsakes follow the same rule?

Each live keepsake ends 30 days after it is created and, as the methodology page states, "carries its own expiry instant on its record; after that instant the reveal link goes dark". Its code encodes a pointer into the database rather than a message, so a recipient who arrives after that instant is handled by the recipient state machine as a missing reveal and lands on "gone" — the same stage as for a reveal that never existed.

Are my photos part of what is kept for 30 days?

No. The saved record contains the project's settings and the composed design; the working photos themselves are not uploaded. In photo-woven mode the state columns record only that the mode was on; the photo-derived artwork is in the composed design, and a reopened project shows its saved artwork and asks for the photo again before re-editing.

Retention is a property of saved rows alone — composition and verification happen in your browser. What a saved record contains, how the expiry is enforced and what the studio does not model are documented on the methodology page; the other guides take each rule in turn.