# Quickstart for AI agents

> HTML version: https://storeshot.art/docs/agents-quickstart/

This page is written for AI coding agents (and the people who run them). Follow the steps in
order: they turn the raw screenshots of an app into every App Store and Google Play image of its
listing with one [screenshot set](/docs/screenshot-sets/). Every command is plain `curl` and `jq`,
and runs as is in one shell session. At the end you will find a prompt to paste into
[Claude Code or Cursor](#prompts-for-claude-code-and-cursor).

Your agent speaks MCP? The [MCP server](/docs/mcp/) offers the same workflow as tools and links
the account without copying a key. Writing TypeScript or JavaScript? The
[TypeScript SDK](/docs/sdk/) does each step in one call. Working in a shell or CI? The
[CLI](/docs/cli/) runs the whole workflow as one `storeshot set` command.

## 1. Check the API key

The key is in the `STORESHOT_API_KEY` environment variable. Send it as
`Authorization: Bearer $STORESHOT_API_KEY` and check it with `GET /me`.

```sh
[ -n "$STORESHOT_API_KEY" ] || echo 'STORESHOT_API_KEY is not set: ask your human for a key'
curl -sS https://api.storeshot.art/v1/me -H "Authorization: Bearer $STORESHOT_API_KEY" \
  | jq '{environment: .credential.environment, scopes: .credential.scopes}'
```

- **Not set, or `401 UNAUTHORIZED`:** no key? Use the device flow: start it with
  `POST /auth/device`, show your human the console link and the code, and poll until they approve
  ([how](/docs/authentication/#link-an-agent-without-a-key)); ask for a test key
  (`"environment":"test"`) to start. Or ask your human to create a key in the developer console
  (https://console.storeshot.art/, API keys) and to export it as `STORESHOT_API_KEY` in the
  environment you run in. A test key (`ss_test_…`) is free.
- Never ask for the key in the chat, never print it, and never write it to a file you commit.
- `environment: "test"`: everything works and costs nothing, but the images are samples of
  exactly the right size ([test mode](/docs/test-mode/)). `"live"`: real images, 1 credit each.

## 2. Capture the raw screenshots

Use the screenshots exactly as the simulator or device captures them: StoreShot adds the
background, the caption and the device, at the sizes each store requires.

- One screenshot per screen, in the order the listing should show them, all in portrait.
- PNG, JPEG or WebP (not HEIC), within the [upload limits](/docs/uploads/#limits).
- iOS Simulator: `xcrun simctl io booted screenshot screen-1.png`. Android emulator or device:
  `adb exec-out screencap -p > screen-1.png`. Screenshots already in the repository are fine too.
- A clean status bar, web builds (Expo, React Native, Flutter) and Maestro flows:
  [Capture raw screenshots](/docs/capture/).
- In a shell, the [CLI](/docs/cli/#capture-the-raw-screenshots) (2.60.0 or later) chains it all:
  `storeshot capture ios` (or `android`, or `web <url>`) once per screen, then
  `storeshot set screens --store app_store,google_play --app-name <name> --dry-run --json` to
  quote, the same without `--dry-run` plus `--out . --layout fastlane` to create, wait and save
  (`storeshot pull <id>` saves a set again later).
- Also collect the app name, a one-line description and a short caption (a few words) per screen.

## 3. Upload them

Upload each file once with `POST /uploads` and keep the ids.

```sh
UPLOAD_IDS=()
for file in screen-1.png screen-2.png; do
  UPLOAD_IDS+=("$(curl -sS https://api.storeshot.art/v1/uploads \
    -H "Authorization: Bearer $STORESHOT_API_KEY" -F "file=@$file" | jq -r .id)")
done
printf '%s\n' "${UPLOAD_IDS[@]}"
```

## 4. Quote the set

Build the request once, then quote it with `?dryRun=true`: it is validated like a real create,
but nothing is created or charged.

```sh
SET_BODY=$(jq -n --arg s1 "${UPLOAD_IDS[0]}" --arg s2 "${UPLOAD_IDS[1]}" '{
  app: {name: "Trailmate", description: "Offline hiking maps"},
  screens: [
    {input: {uploadId: $s1}, caption: "Find every trail"},
    {input: {uploadId: $s2}, caption: "Works offline"}
  ],
  stores: [{store: "app_store"}, {store: "google_play"}]
}')
curl -sS 'https://api.storeshot.art/v1/screenshot-sets?dryRun=true' \
  -H "Authorization: Bearer $STORESHOT_API_KEY" -H 'Content-Type: application/json' \
  -d "$SET_BODY" | jq '{images, credits, availableCredits, sufficient}'
```

The create request (step 5) is the same body without `?dryRun=true`, plus an `Idempotency-Key`:

```http
POST https://api.storeshot.art/v1/screenshot-sets
Authorization: Bearer $STORESHOT_API_KEY
Content-Type: application/json
Idempotency-Key: trailmate-listing-1

{
  "app": {"name": "Trailmate", "description": "Offline hiking maps"},
  "screens": [
    {"input": {"uploadId": "6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"}, "caption": "Find every trail"},
    {"input": {"uploadId": "7a2d3c4b-5e6f-4a7b-8c9d-0e1f2a3b4c5d"}, "caption": "Works offline"}
  ],
  "stores": [{"store": "app_store"}, {"store": "google_play"}]
}
```

- Without `devices`, each store gets the sizes it requires: two App Store sizes and one Google Play
  size, so 2 screens make 6 images. Add `"featureGraphic": true` to the Google Play store for its
  1024 × 500 banner.
- With a live key the set costs `credits` (1 per image). Tell your human the number and
  `availableCredits`, and create the set only after they agree. A test key shows `credits: 0`.
- `sufficient: false` means the create would fail with `402`: ask your human to buy credits.

## 5. Create it and wait

Create the set with an `Idempotency-Key`: if the request times out, send it again with the same
key and body and you get the same set, never a second charge. Use a new key for a new listing.
Then poll every few seconds until `status` is no longer `queued` or `running`.

```sh
SET_ID=$(curl -sS https://api.storeshot.art/v1/screenshot-sets \
  -H "Authorization: Bearer $STORESHOT_API_KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: trailmate-listing-1' -d "$SET_BODY" | jq -r .id)

while :; do
  SET=$(curl -sS "https://api.storeshot.art/v1/screenshot-sets/$SET_ID" \
    -H "Authorization: Bearer $STORESHOT_API_KEY")
  case "$(echo "$SET" | jq -r .status)" in queued|running) sleep 3 ;; *) break ;; esac
done
echo "$SET" | jq '{status, counts, creditsCharged, creditsRefunded}'
```

`succeeded` means every image is ready. `partially_failed` or `failed`: failed images are refunded;
`POST /screenshot-sets/{id}/retry` makes them again (see [Screenshot sets](/docs/screenshot-sets/#retry-cancel-and-delete)).

## 6. Save the images

Download the set as a ZIP in the folders fastlane reads and unzip them in the project root. The
`manifest.json` in the ZIP says whether every image is there.

```sh
curl -sS -o storeshot-fastlane.zip \
  "https://api.storeshot.art/v1/screenshot-sets/$SET_ID/archive?layout=fastlane" \
  -H "Authorization: Bearer $STORESHOT_API_KEY"
unzip -o -q storeshot-fastlane.zip 'fastlane/*'
unzip -p storeshot-fastlane.zip manifest.json | jq '{complete, missing}'
find fastlane -name '*.png' | sort
```

- App Store images land in `fastlane/screenshots/en-US/` (for `fastlane deliver`), Google Play
  images in `fastlane/metadata/android/en-US/images/` (for `fastlane supply`, which replaces the
  screenshots already in the Play listing).
- Without `?layout=fastlane` the ZIP has one folder per store and device
  (`<store>/<device>/<NN>.png`); ask your human where to put them if the project has no fastlane.

## When something goes wrong

Every error has the same shape: `{"error": {"code", "message", "requestId", "docUrl"}}`.

| Status | What to do |
|---|---|
| `401 UNAUTHORIZED` | The key is missing, wrong or revoked: ask your human for a new one. |
| `402 INSUFFICIENT_CREDITS` | Stop. Give your human `details.topUpUrl` to buy credits; do not retry. |
| `400 INVALID_INPUT` | Fix the request: `details` names the field. |
| `409` | The resource is in the wrong state (for example the set is still running): read `message`. |
| `429 RATE_LIMITED`, `429 CONCURRENCY_LIMITED` | Wait the `Retry-After` seconds, then send the same request again. |
| `5xx` | Wait a few seconds and send the same request with the same `Idempotency-Key`. |

When you ask your human (or support) for help, quote the `requestId`. All codes:
[Errors](/docs/errors/).

## Prompts for Claude Code and Cursor

Paste this prompt into your agent from the app's repository, with `STORESHOT_API_KEY` exported in
the environment the agent runs commands in. Edit the stores or captions to taste.

```text
Use the StoreShot API (https://api.storeshot.art/v1) to create my App Store and Google Play screenshots.
Follow the agent quickstart: https://storeshot.art/docs/agents-quickstart.md (all docs: https://storeshot.art/llms.txt).
My API key is in the STORESHOT_API_KEY environment variable; send it as "Authorization: Bearer $STORESHOT_API_KEY".
If it is not set, get one with the device flow (POST /auth/device: show me the link and the code, I approve it
in the console), or ask me to create a key; never print or commit it.

1. Find my app's raw screenshots (simulator or device captures) and its name and one-line description.
2. Upload each screenshot with POST /uploads (multipart field "file").
3. Build one screenshot set for both stores with one short caption per screen. Quote it first with
   POST /screenshot-sets?dryRun=true and tell me how many credits it costs.
4. Create it with POST /screenshot-sets and an Idempotency-Key, poll GET /screenshot-sets/{id} until it
   is final, then download GET /screenshot-sets/{id}/archive?layout=fastlane and unzip it in the
   project root (fastlane folders).
If my key starts with ss_test_ the images are free samples; tell me when to switch to an ss_live_ key.
```

- **Claude Code:** start `claude` in the repository and paste the prompt. It asks before running
  each command unless you allow `curl`, `jq` and `unzip`.
- **Cursor:** open the repository, start a chat in Agent mode and paste the prompt. Its terminal
  must see the variable: export it in your shell profile, then restart Cursor.

To make every future session follow these steps, add this to the project's `CLAUDE.md`,
`AGENTS.md` or Cursor rules:

```text
## Store screenshots
Make App Store and Google Play screenshots with StoreShot:
https://storeshot.art/docs/agents-quickstart.md
The key is in STORESHOT_API_KEY: never print or commit it.
Quote with POST /screenshot-sets?dryRun=true and ask before spending credits with an ss_live_ key.
Save the archive with ?layout=fastlane in the project root.
```

Everything else: [llms.txt](https://storeshot.art/llms.txt), every docs page in one file
([llms-full.txt](https://storeshot.art/llms-full.txt)) and the [API reference](/docs/api/).
