---
title: "request_screenshot_upload"
description: "Signed upload URLs for screenshots that live only on the user’s machine."
canonical: "https://shotops.dev/docs/mcp/reference/request_screenshot_upload"
last_updated: "2026-09-07"
shotops_mcp_version: "0.9.6"
result_contract_version: "2.0.0"
---
# request_screenshot_upload

Signed upload URLs for screenshots that live only on the user’s machine.

## What it does

- Writes
- Account required

How a full-resolution screenshot on somebody’s disk reaches a render without its bytes transiting the conversation. It mints one signed upload URL per slot; you or the user’s shell PUT each raw PNG to its URL, and you pass the returned refs into the render tools. Use it in MCP clients that are not ChatGPT, and in automation, instead of inline base64 for anything real.

You can ask: “Create upload slots for these three Tidebook screenshots.”

## Input

- `count`integerrequiredmin 1max 10 How many upload slots to mint (1–10, one per screenshot).
- `names`arrayoptional Original filenames, one per slot in the same order (e.g. ["03_statistics.png", ...]). Each is encoded into that slot's ref, so the saved project re-loads by real filename even without re-passing `name` at render time. Optional. each item string
- `family`stringoptionalat least 1 characterat most 80 characters Optional screenshot-family coordinate for this batch (for example "android"). Omit for the project's base family. This does not add a renderable output device; it only tags which saved-project screenshot cell each returned slot represents.
- `locale`stringoptionalat least 1 characterat most 80 characters Optional App Store locale coordinate for this batch (e.g. "de-DE"). It is returned on every slot as `variant.locale` and render_project uses it to select that locale's pixels; omit for the project's base locale.

## Result

- `ok`booleanoptional
- `slots`arrayoptional each item object - `ref`stringrequired - `uploadUrl`stringrequiredformat uri - `name`stringoptional - `variant`objectrequired The saved-project screenshot coordinate this slot was minted for. Pass it through with the slot's ref when overriding render_project. - `family`stringoptionalat least 1 characterat most 80 characters Device screenshot family (for example "android"). Omit for the project's base family; the two currently advertised iPhone outputs share that base family. - `locale`stringoptionalat least 1 characterat most 80 characters App Store locale for these pixels. Omit for the project's base locale.
- `instructions`stringoptional
- `disclosure`objectoptional Stated because this call moved caller bytes into ShotOps storage. Relay it before or with the result; do not paraphrase the retention window.
  - `moves`stringrequired The bytes that left the caller’s control, in plain words.
  - `why`stringrequired What the move buys — never a restatement of the mechanism.
  - `retentionDays`integerrequiredgreater than 0 How long ShotOps keeps it without an explicit retaining action. Read from the retention policy, never typed.
  - `retainedBy`stringrequired The explicit action that keeps it past that window.
  - `statement`stringrequired The same four facts as one sentence, safe to relay verbatim.

## Example

That request maps to this call:

```
{
  "name": "request_screenshot_upload",
  "arguments": {
    "count": 3,
    "names": [
      "01-today.png",
      "02-forecast.png",
      "03-spots.png"
    ],
    "locale": "en-US"
  }
}
```

One entry per slot: the signed upload URL to PUT to, and the ref to pass to a render.

## Access and cost

- Needs an account, because the refs are account-scoped storage. Not available to an anonymous hosted caller.
- Free. Minting upload URLs spends no cloud credits.
- The reply discloses what the upload moves, why, how long an unretained upload is kept, and the one action that retains it: saving a project that references it. Say that before the bytes move.
- In ChatGPT, attachments go through import_screenshot instead. A user with a project can skip file transfer entirely and call render_project.

## When it refuses

- **A PUT to the signed URL fails.**
  The URL is short-lived. Mint a fresh batch rather than retrying an expired one.

- **A ref renders as a missing shot.**
  The slot was minted but never uploaded to. Every ref you pass has to have had its PNG PUT to the matching URL first.

## Where this fits

- [Sending refs instead of base64 from CI](https://shotops.dev/docs/mcp/workflows/automation)
- [Getting the screenshots in](https://shotops.dev/docs/mcp/workflows/first-strip)

## What the agent is told

The title and description the server publishes on `tools/list` — this is the copy a model chooses between, reproduced verbatim.

Upload full-size screenshots

Get screenshots that live only on the USER's own machine into a render, at full resolution and without their bytes ever transiting this conversation. It mints signed upload URLs you (or the user's shell) PUT each raw PNG to, and hands back a `ref` per slot to pass into render_strip/emit_bundle. Use it in non-ChatGPT MCP clients and automation, and instead of inline base64 for any real full-resolution screenshot. In ChatGPT, attachments go through import_screenshot instead — and a user with a ShotOps account can skip file transfer entirely by dropping the screenshots into a project and calling render_project. Uploading one batch per store locale? Tag each batch with `locale` (echoed back for your bookkeeping). The result carries a `disclosure` block — what moves into ShotOps storage, why, how long it is kept and the explicit action that retains it. Say that before the bytes move, and relay its numbers rather than your own.
