Reference

render_project

Re-render a saved project — the route that needs nothing attached to the conversation.

View as Markdown

What it does

  • Writes
  • Account required

The account route. By default it renders the screenshots the project already holds, so "render my ShotOps project" is one call with no screenshots argument: no upload, no shell, and the image bytes never enter the chat. That makes it the route that works everywhere, ChatGPT on a phone included. The project supplies the structure — frame order, per-locale caption words, styling — so the result reproduces the designer’s exact strip.

You can ask: “Render the German Tidebook panels from the saved project.”

Input

  • projectstringoptionalat least 1 character

    Which ShotOps project to target: its id (as returned by read_look/save_project), OR its NAME as the user says it — matched exactly, ignoring case and surrounding spaces. Omit = your most recently edited project. A name that matches none or several is refused with the account's projects listed, never resolved by guessing.

  • screenshotsarrayoptionalat most 60 items

    OPTIONAL. OMIT it (or pass []) to render the screenshots the project ALREADY holds — the files the user or designer put in it in the web app — with no upload and no file transfer of any kind. Supply this FLAT list only to OVERRIDE them with your own raw app screenshots: each an inline base64 PNG, { "ref" }, { "url" }, or (local stdio only) { "path" }, and each with a name (its original filename). Object entries may also carry variant: { family?, locale? }; that coordinate overrides the same stored cell, while an entry without variant is the base cell and remains the fallback for every render (the legacy behaviour). Entries are matched to the saved project's shots BY FILENAME (name === the shot's frameName) AND then resolved for each rendered device family + locale, so order does NOT matter and you never pre-sort. A supplied cell wins over the stored one at that coordinate. A shot with neither is reported as missing (it does not error the render). Prefer ref/url for real screenshots so the bytes never transit this conversation.

    each item

    any

    The published schema does not constrain this element, on purpose — a malformed entry comes back as a sentence instead of a stacked union error. What the server re-validates against is below.

  • localestringoptional

    App Store locale, e.g. "de-DE" (default en-US). Labels the render, selects which { "locales": … } screenshot variants render, and, for emit_bundle, selects the fastlane screenshots folder. Does not select caption text — pass the copy for this locale yourself via style.captions[].text (see the README's captions.<locale>.json convention).

  • outputsarrayoptionalat least 1 itemat most 2 items

    The target DEVICES this project is for — iphone-6-9 (1290×2796) and/or iphone-6-5 (1242×2688), in the order they should be offered. On save_project this is saved ON the project (the same Output list the web app shows) and the first entry becomes the previewed size unless panelPresetId says otherwise; on render_project it OVERRIDES the saved set for this call. Omit it and the project's saved outputs are used unchanged.

    each item

    string

    one ofiphone-6-9iphone-6-5

  • previewbooleanoptional

    Render at ~25% resolution for a fast styling preview at a quarter the render cost — small enough to always come back inline. Not for final delivery: re-render without preview (or use emit_bundle directly) once the look is right.

  • outputstringoptional

    How to return rendered bytes: "inline" (base64), "urls" (uploaded, short-lived signed download URLs — use for real/full-resolution renders so bytes never transit this conversation), or omit for auto (inline under ~200KB total, urls above).

    one ofinlineurls

  • waiversarrayoptional

    Approvals for readiness findings this run would otherwise be blocked on. Pass back the exact contract.readiness.findings[] id and digest from a preview: true run, or from the refusal you just received. Each waiver clears ONE finding; fatal findings are never waivable.

    each item

    object

    • findingIdstringrequired

      The contract.readiness.findings[].id this waiver authorizes. Exactly one.

    • findingDigeststringrequired

      That finding’s digest. A digest from an earlier run no longer matches and is rejected as stale.

    • policyVersionstringrequired

      contract.readiness.policyVersion as it stood when the finding was raised.

    • waivedBystringrequired

      The account that approved it. Must be the account making this call.

    • waivedAtstringrequired

      ISO 8601.

    • reasonstringoptional

      Free text, for the receipt. It authorizes nothing on its own.

  • idempotencyKeystringoptionalat least 1 characterat most 200 characters

    Stable retry key for paid Hosted production. Reuse it only for the identical request; a new key deliberately requests a new render. Omit it for deterministic fingerprint retry safety.

What the server actually accepts

The published schema above deliberately accepts a wider shape for screenshots, so a malformed entry comes back as a sentence rather than a stack of union errors. The server re-validates against these shapes and refuses anything else.

  • screenshotsarrayoptionalat most 60 items

    OPTIONAL. OMIT it (or pass []) to render the screenshots the project ALREADY holds — the files the user or designer put in it in the web app — with no upload and no file transfer of any kind. Supply this FLAT list only to OVERRIDE them with your own raw app screenshots: each an inline base64 PNG, { "ref" }, { "url" }, or (local stdio only) { "path" }, and each with a name (its original filename). Object entries may also carry variant: { family?, locale? }; that coordinate overrides the same stored cell, while an entry without variant is the base cell and remains the fallback for every render (the legacy behaviour). Entries are matched to the saved project's shots BY FILENAME (name === the shot's frameName) AND then resolved for each rendered device family + locale, so order does NOT matter and you never pre-sort. A supplied cell wins over the stored one at that coordinate. A shot with neither is reported as missing (it does not error the render). Prefer ref/url for real screenshots so the bytes never transit this conversation.

    each item

    one of 4 shapes

    1string

    Inline base64-encoded PNG (no "data:" prefix). Capped at 3MB of image data — a real full-resolution screenshot is 3-4MB and will NOT fit. Use request_screenshot_upload and pass { ref } instead; inline is for thumbnails and tests.

    2ref

    A ref returned by request_screenshot_upload — resolved server-side, never re-sent inline.

    • refstringrequiredat least 1 character

    • namestringoptionalat most 200 characters

      The screenshot's original filename (e.g. "03_statistics.png"). Pass it so that, if this render is saved as a project, the user's own screenshots folder re-loads exactly this file by name. Optional if the ref already carries a name (from request_screenshot_upload({ names })); omit entirely and the record falls back to a positional name (screen-N.png).

    • variantobjectoptional

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      • familystringoptionalat 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.

      • localestringoptionalat least 1 characterat most 80 characters

        App Store locale for these pixels. Omit for the project's base locale.

    3url

    An https URL to a PNG or JPEG — fetched server-side, converted to PNG when needed (no redirects, ~20MB cap).

    • urlstringrequiredat least 1 character

    • namestringoptionalat most 200 characters

      The screenshot's original filename (e.g. "03_statistics.png"). Pass it so that, if this render is saved as a project, the user's own screenshots folder re-loads exactly this file by name. Optional if the ref already carries a name (from request_screenshot_upload({ names })); omit entirely and the record falls back to a positional name (screen-N.png).

    • variantobjectoptional

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      • familystringoptionalat 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.

      • localestringoptionalat least 1 characterat most 80 characters

        App Store locale for these pixels. Omit for the project's base locale.

    4path

    A local filesystem path to a PNG, read straight off disk — ONLY available over the local stdio server (npx shotops-mcp); the hosted server rejects this entry shape.

    • pathstringrequiredat least 1 character

    • namestringoptionalat most 200 characters

      The screenshot's original filename (e.g. "03_statistics.png"). Pass it so that, if this render is saved as a project, the user's own screenshots folder re-loads exactly this file by name. Optional if the ref already carries a name (from request_screenshot_upload({ names })); omit entirely and the record falls back to a positional name (screen-N.png).

    • variantobjectoptional

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      • familystringoptionalat 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.

      • localestringoptionalat least 1 characterat most 80 characters

        App Store locale for these pixels. Omit for the project's base locale.

Result

  • okbooleanrequired

  • countnumberoptional

  • panelPresetIdstringoptional

  • panelWidthnumberoptional

  • panelHeightnumberoptional

  • renderMsnumberoptional

  • outputstringoptional

    one ofinlineurls

  • localestringoptional

  • nextStepobjectoptional

    • actionstringrequiredalways "offer_save_project"

      An offer to put to the user. Never a command, and never consent already given.

    • toolstringrequiredalways "save_project"

      The door that would persist this work, if they ask for it.

    • projectCreatedbooleanrequiredalways false

      No project was created or updated by this call — the same fact as contract.effects.projectMutation.

    • consentstringrequiredalways "required"

      Saving needs the user’s own preserve/reuse/keep-editing intent. A client that cannot ask returns this offer unresolved rather than saving.

    • reasonstringrequired

  • creditsobjectoptional

    Credits this call spent from the account wallet, the balance left afterwards, and when that wallet is next topped up (ISO 8601; absent when no further refill is scheduled). Renders cost per panel; reads, saves, uploads and deletes are free, as is bundling panels that were already rendered.

    • debitednumberrequired

    • remainingnumberrequired

    • refillAtstringoptional

  • notestringoptional

    Free-text advisories about THIS call, joined into one string. Written when the server did something you did not literally ask for or could not have known: a look/version fallback, an output-mode downgrade, a device this server cannot render, a style.shotLook a look overrode, or placement set by hand where a style.layout template would have produced the same composition. Never an error — the call succeeded. Read it, and act on it next call.

  • layoutobjectoptional

    What is wrong with this render’s COMPOSITION, measured on the panels it just returned. ABSENT means there is nothing to report — a clean strip, or one whose captions could not be measured. Advisory: the panels are already rendered and nothing here changed a pixel.

    • findingsarrayrequired

      each item

      object

      • kindstringrequired

        What overlaps what. Captions must never overlap each other or a device; devices MAY overlap, and are only reported here when the pair does NOT read as composed (no real difference in scale, camera angle or roll).

        one ofcaption-captioncaption-devicedevice-device

      • framesarrayrequired

        0-based indexes into the returned panels. For a caption finding the FIRST entry is the frame whose caption is the one to change; a second appears only when the other party sits in a different frame.

        each item

        number

      • depthPctnumberrequired

        How deep the overlap runs, in whole percent of panel height — or, for device-device, the percentage of the smaller device the other one covers.

      • anchoredbooleanrequired

        true ⟹ a caption was placed by hand (an explicit anchor), so nothing may rewrite it.

      • clearAtScalenumberoptional

        The verified factor that clears this frame’s auto-placed captions — 0.82 means 82% of their current size. Absent when no size above the legibility floor clears it, or when the offending caption is a hand placement.

    • correctedLookanyoptional

      The look this render used, with every caption shrunk to the largest size verified to clear the overlap. Pass it back as look to re-render. Absent when no repair is deterministic — a device-device overlap is a taste call (give the pair a real difference in scale, angle or roll) and a hand-anchored caption is a human placement. note says which.

  • contractobjectoptional

    The versioned ShotOps result contract (#662): what ran, where, what it changed, what it cost, what it produced and — on a refusal — a typed failure over a closed code catalog. Structured content is authoritative; the prose beside it is a rendering of this block.

    • contractVersionstringrequired

      The version of THIS envelope, REPORTED. Compare it against the version you were written for. There is no version negotiation: no tool accepts a requested version, so this is never a refusal — it moves only when a field changes meaning, and additive fields never move it.

    • statusstringrequired

      one ofsucceededpartialfailedrefusedacceptedqueuedrunningcancel_requestedcancelled

    • terminalbooleanrequired

      false ⟹ this operation is still running and will be reported again (#666).

    • operationobjectrequired

      • idstringrequired

        Identity for THIS call, unique per invocation. Quote it in a bug report.

      • toolstringrequired

        The registered tool name that produced this result.

      • kindstringrequired

        one ofrenderbundlereadmutateuploaddeletestatus

      • durableobjectoptional

        #666 domain handle for addressable long-running work. Absent from synchronous calls.

        • operationIdstringrequired

        • pollWithstringoptional

    • executionobjectrequired

      • locationstringrequired

        Where the work RAN. local is the caller’s own machine over stdio.

        one oflocalhosted

      • inputModesarrayrequired

        Every way this door accepts screenshots and assets.

        each item

        string

        one ofinline_base64stored_refremote_urllocal_pathproject_stored

      • deliveryModesarrayrequired

        Every way this door can hand a result back.

        each item

        string

        one ofinline_base64signed_urllocal_pathshare_link

    • effectsobjectrequired

      What this call DID to the world, independent of what it returned. Every flag is stated on every result, false included: an absent flag would be indistinguishable from an effect nobody thought to declare.

      • networkFetchbooleanrequired

        This call fetched bytes from a host neither ShotOps nor the caller controls.

      • uploadbooleanrequired

        Caller bytes were uploaded into ShotOps storage.

      • retainedStoragebooleanrequired

        Something survives this call in ShotOps storage.

      • projectMutationbooleanrequired

        A saved project or its look history changed.

      • publicationbooleanrequired

        Something became reachable outside the account — a share link.

      • deletionbooleanrequired

        Something was permanently removed.

    • costobjectrequired

      • unitstringrequiredalways "credit"

        Public cloud credits — the same unit every ShotOps surface quotes.

      • modelstringrequired

        How this connection pays. unmetered is local stdio, which renders on the caller’s own machine; anonymous_allowance is the unsigned hosted taste.

        one ofmeteredanonymous_allowanceunmetered

      • estimatednumberoptional

        What the call was expected to cost, before it ran.

      • reservednumberoptional

        Held against the wallet for the duration of the call (#666).

      • releasednumberoptional

        Given back — an unused reservation or a refund after a post-charge failure (#666).

      • settlednumberoptional

        Actually taken. Absent when nothing was charged.

      • balanceAfternumberoptional

        The wallet’s public cloud credit balance once this call settled.

      • refillAtstringoptional

        ISO 8601. When the wallet is next topped up; absent when none is scheduled.

    • progressobjectoptional

      Persisted monotonic durable-operation progress. Never inferred from transient render metadata. Relay panelsCompleted/panelsTotal and the estimate to the waiting user; the item counters are internal bookkeeping and do not match what they asked for.

      • completedintegerrequiredmin 0

        Internal scheduling items done — panels PLUS the bundle and result items. Do not quote this to a person.

      • totalintegerrequiredmin 0

        Internal scheduling items in total. Larger than the panel count.

      • panelsCompletedintegeroptionalmin 0

        Panels finished, in the unit the caller asked in. THIS is the number to report.

      • panelsTotalintegeroptionalmin 0

        Panels this operation will produce — the count the user asked for.

      • attemptintegerrequiredmin 0

      • heartbeatAtstring or nulloptional

      • estimatedRemainingSecondsnumberoptionalmin 0

        Rough seconds of rendering left, from this operation’s own measured pace once a panel has landed. An estimate, not a deadline — say “about”. Absent when nothing is left to render.

    • resolvedInputobjectoptional

      #661 — what the server actually resolved the request to, before any pixel was produced.

      • snapshotIdstringoptional

        psi_ + the first 32 hex of the fingerprint. Quote it in a bug report.

      • fingerprintstringoptional

        sha256 over every resolved fact. Equal fingerprints ⟹ equal production input.

      • sourcesarrayoptional

        One entry per source cell, resolved or not. slot is the cell the bytes came FROM — a shot id and coordinate on a saved project, an ordered slot label on a direct render. Never a filename, a ref or a URL. origin is absent exactly when status is not resolved: there is nothing the cell came from.

        each item

        object

        • slotstringrequired

        • statusstringrequired

          one ofresolvedmissingambiguous

        • originstringoptional

          one ofinline_base64stored_refremote_urllocal_pathproject_stored

      • defaultsAppliedarrayoptional

        What the server chose because the caller said nothing.

        each item

        string

      • overridesarrayoptional

        What the caller said that changed the outcome.

        each item

        string

    • readinessobjectoptional

      #665 — the readiness verdict and any waiver receipt.

      • statestringoptional

        blocked ⟹ nothing was delivered. ready_with_findings ⟹ delivered, and here is what to know.

        one ofreadyready_with_findingswaivedblocked

      • policyVersionstringoptional

        The readiness policy this verdict was computed under. A waiver granted under another one is rejected.

      • findingsarrayoptional

        Every finding, with code, waivable and digest. A waiver names one id AND its digest; there is no wildcard and no code-level waiver.

        each item

        object

        • idstringrequired

          rf_ + 24 hex. Quote it in a waiver.

        • severitystringrequired

          one ofinfowarnblock

        • messagestringrequired

        • targetobjectrequired

          The exact shot, panel, locale, output or frames this finding is about.

          • shotIdstringoptional

          • panelIndexnumberoptional

          • localestringoptional

          • outputstringoptional

          • captionLayernumberoptional

          • framesarrayoptional

            each item

            number

        • evidenceobjectrequired

          Bounded redacted facts used to derive the finding and its digest.

      • waiversarrayoptional

        The waivers this call ACCEPTED. Pass these objects back verbatim to reuse them; a rebuilt one is rejected.

        each item

        object

        • findingIdstringrequired

        • findingDigeststringrequired

        • policyVersionstringrequired

        • waivedBystringrequired

        • waivedAtstringrequired

        • codestringrequired

          one ofsource_identity_ambiguousoutput_unsupportedoutput_omittedlook_exact_unavailableno_panelspanel_invalid_pngpanel_dimensions_invalidrenderer_failedinput_changed_during_runpanel_source_emptylocale_source_fallbackdevice_source_fallbackcaption_inheritedcaption_locale_fallbacklook_source_fallbackcaption_device_collisioncaption_caption_collisiondevice_device_collisioncaption_legibility_unresolvedscope_filteredlocale_not_live_on_applelook_hold_inactivecaption_text_empty

        • reasonstringoptional

    • artifactsarrayoptional

      each item

      object

      • idstringrequired

        The opaque asset id from the custody registry (#663) — or, for kind: "share", the link’s own token. Never a storage path, and never a signed URL.

      • kindstringrequired

        one ofpanelbundlescreenshotshareproject

      • deliveryobjectrequired

        • modestringrequired

          one ofinline_base64signed_urllocal_pathshare_link

        • urlstringoptional

          THE ONE FIELD a signed URL may appear in. Nothing else in this envelope carries one.

        • expiresAtstringoptional

          ISO 8601, when the grant above stops working.

      • bytesnumberoptional

      • retainedUntilstringoptional

        ISO 8601, when ShotOps stops keeping the artifact itself.

    • failureobjectoptional

      Present exactly when status is failed or refused.

      • codestringrequired

        The closed failure code. Branch on this, never on the sentence.

        one ofinvalid_inputunsupported_inputproject_not_foundproject_ambiguousno_projectsversion_not_foundresolution_incompletereadiness_requiredreadiness_unmetasset_not_foundasset_in_useauthentication_requiredplan_requiredtrial_choice_requiredquota_exhaustedanonymous_limit_reachedcapability_deniedbilling_unavailablerender_failedstorage_failedpersistence_failedupstream_unavailablepayload_too_largerate_limitedoperation_not_foundidempotency_conflictoperation_unavailableoperation_not_readycontract_version_unsupportedinternal_error

      • phasestringrequired

        How far the call got. input and authorization guarantee nothing was rendered, stored, written or charged.

        one ofinputauthorizationresolutionreservationexecutiondeliverypersistence

      • retryablebooleanrequired

        true ⟹ the identical call may succeed later with nothing changed.

      • nextActionstringrequired

        The one move that resolves this, machine-readable.

        one ofnonefix_inputchoose_projectupload_assetsreduce_scopesign_inchoose_planupgrade_planretrywait_and_retrypoll_operationupgrade_clientcontact_support

      • detailsobjectoptional

        Bounded, redacted, code-specific facts (the offending field name, the candidate project ids, the required plan). Never a credential, a signed URL or screenshot bytes.

  • panelsarrayoptional

    each item

    one of 3 shapes

    1string

    2ref

    • refstringrequired

    • urlstringrequiredformat uri

    3position

    • positionintegerrequiredmin 1

    • contentIndexintegerrequiredmin 1

    • mimeTypestringrequiredalways "image/png"

    • deliverystringrequired

      one ofimageresource_link

    • refstringoptional

  • projectobjectoptional

    • idstringrequired

    • namestringrequired

  • openUrlstringoptionalformat uri

  • shotsarrayoptional

    each item

    object

    • frameNamestringrequired

    • matchedbooleanrequired

    • requestedVariantobjectoptional

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      • familystringoptionalat 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.

      • localestringoptionalat least 1 characterat most 80 characters

        App Store locale for these pixels. Omit for the project's base locale.

    • resolvedAtone of 2 shapesoptional

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      1object

      Which screenshot cell this image belongs to. Empty {} is the project's base device family + base locale; a missing cell inherits through ShotOps' pixel fallback rules.

      • familystringoptionalat 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.

      • localestringoptionalat least 1 characterat most 80 characters

        App Store locale for these pixels. Omit for the project's base locale.

      2null

    • inheritedarrayoptional

      each item

      string

      one offamilylocale

    • sourcestringoptional

      one ofsuppliedproject

  • outputsarrayoptional

    each item

    object

    • deviceIdstringrequired

    • labelstringrequired

    • panelPresetIdstringrequired

    • panelWidthnumberrequired

    • panelHeightnumberrequired

    • countnumberrequired

    • panelsarrayrequired

      each item

      one of 3 shapes

      1string

      2ref

      • refstringrequired

      • urlstringrequiredformat uri

      3position

      • positionintegerrequiredmin 1

      • contentIndexintegerrequiredmin 1

      • mimeTypestringrequiredalways "image/png"

      • deliverystringrequired

        one ofimageresource_link

      • refstringoptional

  • operationIdstringoptional

  • statusstringoptional

    one ofacceptedqueuedrunningcancel_requestedsucceededfailedcancelled

  • progressobjectoptional

    • completednumberrequired

    • totalnumberrequired

    • panelsCompletednumberoptional

      Panels finished — the number to report to the user.

    • panelsTotalnumberoptional

      Panels this operation will produce.

    • attemptnumberrequired

    • heartbeatAtstring or nulloptional

    • estimatedRemainingSecondsnumberoptional

      About how many seconds of rendering are left. An estimate — say “about”.

  • failureanyoptional

Example

That request maps to this call:

{
  "name": "render_project",
  "arguments": {
    "project": "Tidebook 2.4 launch",
    "locale": "de-DE",
    "preview": true
  }
}

One PNG per panel at every device size the project targets, plus a per-shot report saying which shots matched a screenshot and which are missing.

Access and cost

  • Needs an account. Not available to an anonymous hosted caller.
  • Preview is available to any signed-in account. A full-resolution render is production output and needs an active Pro trial or Pro.
  • Hosted pricing is the same as render_strip: 1 cloud credit per preview panel, 4 per full-resolution panel. Local rendering is free.
  • A project holds its own screenshots when its owner set its reach to "Your account" in the web app. read_project reports exactly that.

When it refuses

Shots come back reported as missing.
That is a report, not an error — the project has a frame with no stored or supplied screenshot. Supply that file, or accept the strip without it.
You supplied screenshots and they landed on the wrong frames.
Supplied files are matched to the project’s shots by filename, so the names have to be the originals. Order is irrelevant; the name is everything.
The reply says production output is not available.
Nothing rendered, nothing charged. Take the step account_status names, then retry — or render a preview in the meantime.

Where this fits

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.

Render a saved project

Re-make the store screenshots for a strip the user already has in ShotOps — the route that needs NOTHING attached to this conversation. By default it renders the screenshots the project ALREADY holds, so "render my ShotOps project" is one call with NO `screenshots` argument: no upload, no PUT, no shell, and the image bytes never enter the chat. That makes it the route that works everywhere, ChatGPT on mobile included. A project holds them whenever its owner set its reach to "Your account" in ShotOps, and read_project reports exactly that as `screenshots.available` — check there rather than assuming the files have to be re-supplied. Supply `screenshots` only to OVERRIDE them with your own raw files: a FLAT list, each with its original filename as `name`, matched to the project's shots BY filename — so ORDER does not matter, you never pre-sort, and a supplied file wins over the stored one. The project supplies the structure — frame order, per-locale caption words (pick one with `locale`), styling — so the result reproduces the designer's exact strip, at every device size the project targets (`outputs` overrides that for this call). Shots with neither a stored nor a supplied screenshot are reported as missing, not an error. Returns per-panel PNGs (`inline` or `urls`). No store credential.