Skip to main content
A screenshot of a real device in use can contain anything that was on that screen: a signed in app, a message, a personal detail. Frames are therefore treated as the most sensitive thing the control plane touches. Frame storage changed on 2026 09 08. What follows describes the behaviour in force now.

Frames are kept for as long as your organisation is

There is no expiry and no sweep. Nothing in the service deletes a frame, and that is structural rather than a policy anybody has to remember: a frame is stored with no expiry time at all, so no rule that compares a deadline can select one. This is what makes step by step replay possible. The frames a run saw are the run’s own record, and a record that disappears after three days is not a record you can be shown a month later when it matters.

There is no API that reads a frame back today

The two endpoints that returned them, GET /v1/runs/{agentRunId}/frames and GET /v1/receipts/{idempotencyKey}/frame, have been withdrawn. Nothing consumed them, and an endpoint published on this site that the service cannot serve is worse than an absent one. The archive itself is unchanged: frames are still captured, still written, and still kept for as long as your organisation is. What follows describes what is stored, not something you can fetch. If you need a run’s frames, ask.

The store is private, and any read path would be a URL we mint

The store is private in its own right: an anonymous request, a request carrying the public API key, and a request from a signed in browser session are all refused, including the request that only asks whether an object exists. A read path returns a signed URL for one image rather than pixels, valid for the number of seconds the response names and never longer than five minutes. Treat such a URL as a password for that one image while it lasts: do not store it, log it, or paste it into a bug report. Before any URL is signed, the frame is looked up in the database under your organisation’s own row level policy, and the storage location is read out of that row. It is never constructed from the values in a request. That distinction is the authorisation: a location built from arguments would be a valid looking location for somebody else’s frame, and object storage has no idea which tenant asked.

What we do not do to a frame

Frames are not encrypted at the application layer, and the reason is worth being straight about rather than implying more protection than exists. The protection is the private bucket, a credential whose reach is object storage and not the database, the organisation check above, and the short lived URL. There is no per organisation key, and per frame crypto shredding is not available today.

Turning archiving off

An organisation can be taken out of frame archiving entirely, and can instead choose a finite retention window with a ceiling of 90 days. Both are set through the operational management path, not by you and not by an agent; ask if you want either. Two things about those settings:
  • Leaving the archive does not erase what is already in it. It stops new frames being written.
  • A zero day window means the bytes are never written, not written and then cleaned up shortly after. At that setting the control plane records that a frame was captured and stores no image, so there is nothing waiting on a cleanup process to run on time.
An organisation on a finite window has frames that stop being readable when that window passes. The database gate that decides readability is what enforces it, so a frame past its window is served by no path, and a signed URL for one is cut short so that it cannot outlive the window either.

Retention is decoupled from billing, permanently

Frame bytes do not participate in billing, usage or quota, and never have. The billing pipeline derives from the receipt ledger, which contains no frames. This is not a coincidence maintained by care. The two read and write completely disjoint paths, and that is checked directly: billing derived with frame data present and billing derived with it gone produce identical results. The consequence that matters to you: what happens to your frames can never cost you a billing record, so “we need it for billing” can never become an argument about images.

What the control plane keeps besides the image

A frame’s identity is tracked separately from its bytes, and was long before the bytes were stored:
  • the frame id you can echo back as observedFrame,
  • a per device sequence number,
  • when it was registered, and the geometry it was captured at.
That is what makes it possible to refuse an action decided on a screen that has already changed. The receipt ledger holds no frame bytes. screenshot is not one of the actions a receipt records, and the summaries that are recorded are deliberately thin: a typed string is recorded as its length, never its content.

An agent cannot configure its own retention

Retention configuration is not an MCP tool, and this is a structural decision rather than a backlog item. An agent that could shorten its own audit retention could erase the record of what it did. That defeats the purpose of having a record. So the configuration surface is not reachable from the tool surface at all: it is written through the operational management path, under the same org pinning and row level policies as everything else. Cross org access to that configuration is blocked by the same two independent guards described in Org isolation, and the absence of a configuration tool on the MCP surface is checked rather than assumed.

In a private deployment

Where the device gateway runs in your own environment, frames never leave it. The same schema travels with the gateway, and you hold the storage. Phonebase does not reach into your environment to read or remove your data, in the same way it does not reach in to meter it. That deployment shape is a seam that is kept clean, not a product you can buy today.