Quickstart for AI agents
Exact steps for Claude Code, Cursor and other coding agents - check the key, upload raw screenshots, quote and create a screenshot set, poll it and save the images in fastlane folders.
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. 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.
Your agent speaks MCP? The MCP server offers the same workflow as tools and links
the account without copying a key. Writing TypeScript or JavaScript? The
TypeScript SDK does each step in one call. Working in a shell or CI? The
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.
[ -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 withPOST /auth/device, show your human the console link and the code, and poll until they approve (how); 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 asSTORESHOT_API_KEYin 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)."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.
- 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.
- In a shell, the CLI (2.60.0 or later) chains it all:
storeshot capture ios(orandroid, orweb <url>) once per screen, thenstoreshot set screens --store app_store,google_play --app-name <name> --dry-run --jsonto quote, the same without--dry-runplus--out . --layout fastlaneto 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.
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.
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:
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": trueto 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 andavailableCredits, and create the set only after they agree. A test key showscredits: 0. sufficient: falsemeans the create would fail with402: 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.
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).
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.
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/(forfastlane deliver), Google Play images infastlane/metadata/android/en-US/images/(forfastlane supply, which replaces the screenshots already in the Play listing). - Without
?layout=fastlanethe 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.
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.
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
claudein the repository and paste the prompt. It asks before running each command unless you allowcurl,jqandunzip. - 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:
## 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, every docs page in one file (llms-full.txt) and the API reference.