situ.md
# Situ

> Photographic mockups of an app screen. Send a screenshot or screen recording, get a free watermarked preview in seconds, and pay once for the HD file.

- Website: https://situ.supply
- This guide as plain text: https://situ.supply/agent.md
- No account and no API key. Payment is a Stripe checkout page that a person completes.

## MCP

Agents that speak MCP can connect to `https://situ.supply/mcp` (streamable HTTP, no key). The tools are `list_scenes`, `start_upload`, `create_mockup` and `get_mockup`, and they follow the workflow below.

## What it is for

Situ puts an app's screen onto a photograph of an iPhone lying in natural light: sun on a wooden table, leaf shadows that move across the phone and the screen. Use it when the user wants their app to look real and alive in an App Store or Google Play listing, a landing page, a launch post or a pitch deck.

A screenshot on a still scene gives an HD image. A screen recording gives an HD video as long as the part of the recording you choose.

The scenes are AI-generated photographs. Code, not AI, measures each screen against a real iPhone and fits the user's screen to it. Uploads are never used for training.

## Workflow for coding agents

1. Read the scenes and prices: `GET /api/scenes`.
2. Get the screen. Use a file the user gives you, or record their app (see "Recording the screen" below). 5 to 15 seconds of one flow reads best.
3. Email is optional: Stripe asks for it at payment. If you pass one, get it from the user; never invent one. Ask before you set `marketing=true`.
4. Make the preview: `POST /api/jobs`. Keep the `id` that comes back.
5. Poll `GET /api/jobs/{id}` every 2 seconds until `status` is `ready`. A preview takes about 3 seconds for an image and up to 30 seconds for a long recording.
6. Show the user the preview at `preview`, and give them the job page `https://situ.supply/j/{id}`.
7. If they want the HD file, give them the `pay` link. The person pays on Stripe. Never enter card details yourself.
8. After payment, `status` goes `paid`, then `rendering`, then `done`. Then download the file at `download`.

Files are deleted 7 days after the job is made.

## Scenes

- `tray-a`: tray-a. A screenshot gives an image, a recording gives a video.
- `canvas-a`: canvas-a. A screenshot gives an image, a recording gives a video.
- `shelf-a-moving`: shelf-a-moving. A 12 s video scene: every result is a video.
- `tray-a-moving`: tray-a-moving. A 16 s video scene: every result is a video.
- `tray-b`: tray-b. A screenshot gives an image, a recording gives a video.
- `bedside-a`: bedside-a. A screenshot gives an image, a recording gives a video.
- `bedside-b`: bedside-b. A screenshot gives an image, a recording gives a video.
- `matcha-a`: matcha-a. A screenshot gives an image, a recording gives a video.
- `matcha-b`: matcha-b. A screenshot gives an image, a recording gives a video.
- `rain-1`: rain-1. A screenshot gives an image, a recording gives a video.
- `canvas-a-moving`: canvas-a-moving. A 16 s video scene: every result is a video.
- `canvas-b`: canvas-b. A screenshot gives an image, a recording gives a video.
- `leaf-a`: leaf-a. A screenshot gives an image, a recording gives a video.
- `leaf-b`: leaf-b. A screenshot gives an image, a recording gives a video.
- `studio-6`: studio-6. A screenshot gives an image, a recording gives a video.
- `sunbar-a`: sunbar-a. A screenshot gives an image, a recording gives a video.
- `dusk-4`: dusk-4. A screenshot gives an image, a recording gives a video.
- `shelf-a`: shelf-a. A screenshot gives an image, a recording gives a video.
- `shelf-b`: shelf-b. A screenshot gives an image, a recording gives a video.
- `tar-a`: tar-a. A screenshot gives an image, a recording gives a video.
- `tar-b`: tar-b. A screenshot gives an image, a recording gives a video.
- `relic-b`: relic-b. A screenshot gives an image, a recording gives a video.
- `eames-1`: eames-1. A screenshot gives an image, a recording gives a video.

## Prices

The preview is free: 720 px, watermarked, for evaluation only.

- HD mockup image: £9
- HD mockup video, up to 15 s: £19
- HD mockup video, up to 30 s: £29
- HD mockup video, up to 60 s: £39
- Scene pass, up to 100 HD files on one scene for 12 months: £49

A recording is priced by the length you choose with `start` and `end`, so trim it to pay less. Recordings longer than 60 seconds must be trimmed.

## API

All paths are relative to https://situ.supply. Errors return JSON `{"error": "<message for the user>"}` with status 400 (bad input), 404 (unknown or expired job) or 413 (file over 300 MB).

### GET /api/scenes

```json
{
  "scenes": [
    {
      "name": "tray-a",
      "label": "Tray 1",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "canvas-a",
      "label": "Canvas 1",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "shelf-a-moving",
      "label": "Candle 1, moving",
      "collection": "Night",
      "kind": "video",
      "seconds": 12.0,
      "about": ""
    },
    {
      "name": "tray-a-moving",
      "label": "Tray 1, moving",
      "collection": "Morning",
      "kind": "video",
      "seconds": 16.0,
      "about": ""
    },
    {
      "name": "tray-b",
      "label": "Tray 2",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "bedside-a",
      "label": "Bedside 1",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "bedside-b",
      "label": "Bedside 2",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "matcha-a",
      "label": "Matcha 1",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "matcha-b",
      "label": "Matcha 2",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "rain-1",
      "label": "Rain",
      "collection": "Morning",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "canvas-a-moving",
      "label": "Canvas 1, moving",
      "collection": "Sunlight",
      "kind": "video",
      "seconds": 16.0,
      "about": ""
    },
    {
      "name": "canvas-b",
      "label": "Canvas 2",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "leaf-a",
      "label": "Leaf 1",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "leaf-b",
      "label": "Leaf 2",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "studio-6",
      "label": "Studio",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "sunbar-a",
      "label": "Plaster",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "dusk-4",
      "label": "Hand",
      "collection": "Sunlight",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "shelf-a",
      "label": "Candle 1",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "shelf-b",
      "label": "Candle 2",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "tar-a",
      "label": "Tar 1",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "tar-b",
      "label": "Tar 2",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "relic-b",
      "label": "Rock 2",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    },
    {
      "name": "eames-1",
      "label": "Leather chair",
      "collection": "Night",
      "kind": "still",
      "seconds": 0,
      "about": ""
    }
  ],
  "tiers": [
    {
      "id": "image",
      "name": "HD mockup image",
      "seconds": 0,
      "pence": 900,
      "price": "£9"
    },
    {
      "id": "video15",
      "name": "HD mockup video, up to 15 s",
      "seconds": 15,
      "pence": 1900,
      "price": "£19"
    },
    {
      "id": "video30",
      "name": "HD mockup video, up to 30 s",
      "seconds": 30,
      "pence": 2900,
      "price": "£29"
    },
    {
      "id": "video60",
      "name": "HD mockup video, up to 60 s",
      "seconds": 60,
      "pence": 3900,
      "price": "£39"
    }
  ],
  "maxSeconds": 60
}
```

### POST /api/jobs

Multipart form fields:

- `upload` (instead of `file`): an id from `POST /api/uploads`, to try several scenes without uploading again.
- `file_url` (instead of `file`): a public https link to the screenshot or recording, for agents that can't send a file.
- `file`: a screenshot (PNG, JPEG or WebP, up to 8000 px) or a recording (MP4, MOV, M4V, WebM or MKV, up to 300 MB). A portrait iPhone shape, about 0.46 wide to tall (for example 1206 × 2622), fits exactly. Other shapes are cropped from the centre.
- `scene` (required): a scene `name` from `/api/scenes`.
- `email` (optional): the user's email address, prefilled at payment. Stripe asks for it if you leave it out.
- `start`, `end` (optional): the part of a recording to use, in seconds. At least 0.5 s, at most 60 s.
- `marketing` (optional, default `false`): `true` only if the user agrees to emails about new scenes.
- `source` (optional): send `api`, so we know the job came from an agent, or `minds` from a Minds skill.

```bash
curl -s https://situ.supply/api/jobs \
  -F file=@recording.mp4 \
  -F scene=oak \
  -F start=0 -F end=8 \
  -F source=api
```

```json
{"id": "<job id>"}
```

### POST /api/uploads

Multipart with one field, `file` (same rules as above). Returns `{"upload": "<id>", "kind": "image" | "video", "seconds": 8.0}`.
Pass the id as `upload` to `POST /api/jobs` once per scene you want to try. Uploads are deleted after 7 days.

### GET /api/jobs/{id}

```json
{
  "id": "<job id>",
  "status": "ready",
  "scene": "oak",
  "kind": "video",
  "seconds": 8.0,
  "tier": {"name": "HD mockup video, up to 15 s", "price": "£19"},
  "expires": 1793900000,
  "preview": "/files/<job id>/preview",
  "previewKind": "video",
  "pay": "https://buy.stripe.com/..."
}
```

- `status`: `queued`, `ready` (preview done, not paid), `paid`, `rendering` (HD file), `done`, `failed` or `expired`.
- `preview`: the watermarked preview while unpaid, the HD file once `done`. `previewKind` is `image` or `video`.
- `pay`: present while `status` is `ready`.
- `download`: present when `status` is `done`.
- `expires`: Unix time when the files are deleted.

### Files

- `GET /files/{id}/preview`: the 720 px watermarked preview (JPEG or MP4).
- `GET /files/{id}/hd`: the HD file, shown inline. Only after payment.
- `GET /files/{id}/download`: the HD file as an attachment (PNG or MP4). Only after payment.

## Recording the screen

- iOS Simulator screenshot: `xcrun simctl io booted screenshot screen.png`
- iOS Simulator recording: `xcrun simctl io booted recordVideo --codec=h264 recording.mp4`, then stop it with Ctrl-C.
- Android device or emulator: `adb shell screenrecord /sdcard/recording.mp4`, stop it with Ctrl-C, then `adb pull /sdcard/recording.mp4`.

Record in portrait. Hide personal data, notifications and test credentials before you record.

## Rules

- The job id is the only key to the files. Share it only with the user.
- Upload only screens the user has the right to use.
- Do not pay on the user's behalf. Send them the `pay` link.
- Previews are watermarked and are not for publishing.
- The HD file comes with a commercial licence: App Store and Google Play listings, websites, ads, social posts, videos and pitch decks. Terms: https://situ.supply/terms. Privacy: https://situ.supply/privacy.

## Prompts to try

Make a mockup of my app:

```text
Record a 10-second flow of my app in the iOS Simulator, then make a mockup video with https://situ.supply/agent.md. Show me the preview and the payment link.
```

Make a store screenshot:

```text
Take a screenshot of the home screen in the simulator and put it on the oak scene with https://situ.supply/agent.md. Ask me for my email first.
```