> ## Documentation Index
> Fetch the complete documentation index at: https://scrinly.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Transform a stored screenshot

> Resize, crop and re-encode a screenshot you already captured — no recapture, no AI. Pick a `preset` or set `width` / `height` / `format`, and optionally cut a `region` or `bounds` first. One credit per output; verified cache hits are refunded.



## OpenAPI

````yaml /api-reference/openapi.json post /render/screenshot/{id}/transform
openapi: 3.1.0
info:
  title: Scrinly Browser API
  version: 1.0.0
  description: >-
    Public screenshot, region, Visual Style Guide, visual diff, monitoring,
    batch, job-status, and usage API.
servers:
  - url: https://api.scrinly.com
    description: Production
security:
  - BearerAuth: []
tags:
  - name: Screenshots
  - name: Batches
  - name: Diffs
  - name: Jobs
  - name: Usage
  - name: Monitors
  - name: Recipients
paths:
  /render/screenshot/{id}/transform:
    post:
      tags:
        - Screenshots
      summary: Transform a stored screenshot
      description: >-
        Resize, crop and re-encode a screenshot you already captured — no
        recapture, no AI. Pick a `preset` or set `width` / `height` / `format`,
        and optionally cut a `region` or `bounds` first. One credit per output;
        verified cache hits are refunded.
      operationId: transformScreenshot
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            pattern: ^scr_
          description: An unexpired screenshotId belonging to the authenticated account.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransformRequest'
            example:
              preset: og-image
              region: region-1
            examples:
              preset:
                summary: OG image of a region
                value:
                  preset: og-image
                  region: region-1
              custom:
                summary: Custom size and format
                value:
                  width: 800
                  format: png
              several:
                summary: Several outputs from one region
                value:
                  region: region-1
                  outputs:
                    - og-image
                    - thumbnail
                    - preset: square
                      format: png
                  async: true
      responses:
        '200':
          description: All outputs completed
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TransformSuccess'
                  - $ref: '#/components/schemas/TransformBatch'
          headers:
            X-Scrinly-Storage-URL:
              description: Direct URL; single-output success only.
              schema:
                type: string
                format: uri
        '202':
          $ref: '#/components/responses/AsyncAccepted'
        '207':
          description: Partial success with per-output settlements
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformBatch'
        '400':
          description: Invalid request, no charge
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '410':
          description: Owned reference expired or unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/TooLarge'
        '422':
          description: All outputs failed, with settled credits
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/TransformBatch'
        '503':
          description: >-
            Infrastructure unavailable; ambiguous settlement supplies a polling
            job ID rather than claiming a refund
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    TransformRequest:
      type: object
      additionalProperties: false
      description: >-
        Describe one output at the top level, or several in `outputs`. With
        `outputs`, top-level fields are defaults for every output; an output
        that says what to cut replaces the default `region`/`bounds`.
      properties:
        preset:
          $ref: '#/components/schemas/TransformPreset'
        region:
          type: string
          description: >-
            Cut this saved region (e.g. `region-1`) from the screenshot.
            Requires a capture with `regions=true`.
        part:
          oneOf:
            - type: string
            - type: integer
              minimum: 0
          description: >-
            With `region`: one part of it, by id (`region-1-part-0`) or index
            (`0` = first). Original screenshot pixels.
        savedCrop:
          type: boolean
          default: false
          description: >-
            With `region` + `part`: use the stored crop file instead of cutting
            original pixels. Use it for pages whose whole screenshot exceeds 64
            MP or 24 MiB.
        bounds:
          oneOf:
            - type: string
              pattern: ^\d+,\d+,\d+,\d+$
              description: '`x,y,width,height` in screenshot pixels'
            - $ref: '#/components/schemas/TransformBounds'
          description: Cut an arbitrary rectangle. Cannot be combined with `region`.
        width:
          type: integer
          minimum: 1
          maximum: 32768
          description: Output width. Give one side to keep the aspect ratio.
        height:
          type: integer
          minimum: 1
          maximum: 32768
          description: Output height.
        fit:
          type: string
          enum:
            - contain
            - cover
            - fill
          description: >-
            How to fit both sides: `contain` may pad, `cover` may crop, `fill`
            may stretch. Default `contain`.
        background:
          type: string
          pattern: ^#[0-9a-fA-F]{6}$
          description: Padding / transparency fill for `contain` and JPEG.
        format:
          type: string
          enum:
            - jpeg
            - png
            - webp
          description: 'Output encoding. Default: same family as the source.'
        quality:
          type: integer
          minimum: 1
          maximum: 100
          description: Lossy quality. Default 80 (or the preset's).
        outputs:
          type: array
          minItems: 1
          maxItems: 16
          items:
            oneOf:
              - $ref: '#/components/schemas/TransformPreset'
              - $ref: '#/components/schemas/TransformOutputSpec'
          description: >-
            Several outputs in one job, one credit each. Each is a preset name
            or an object; top-level fields apply to all. Returns ordered
            `results`.
        cache:
          type: boolean
          default: false
        async:
          type: boolean
          default: false
        store:
          const: true
        webhookUrl:
          type: string
          format: uri
        webhookSecret:
          type: string
          writeOnly: true
        storage_path:
          type: string
          default: transforms
        storage_provider:
          type: string
          enum:
            - b2
            - s3
            - r2
        storage_bucket:
          type: string
        storage_endpoint:
          type: string
          format: uri
          writeOnly: true
        storage_region:
          type: string
        storage_access_key_id:
          type: string
          writeOnly: true
        storage_secret_access_key:
          type: string
          writeOnly: true
        storage_application_key_id:
          type: string
          writeOnly: true
        storage_application_key:
          type: string
          writeOnly: true
        storage_public_domain:
          type: string
          format: uri
        storage_return_location:
          type: boolean
          default: false
        storage_bucket_id:
          type: string
        storage_bucket_name:
          type: string
        storage_account_id:
          type: string
        storage_class:
          type: string
          enum:
            - standard
            - reduced_redundancy
            - standard_ia
            - onezone_ia
            - intelligent_tiering
            - glacier
            - deep_archive
            - outposts
            - glacier_ir
        storage_acl:
          type: string
          enum:
            - private
            - public-read
    TransformSuccess:
      type: object
      required:
        - success
        - type
        - assetId
        - screenshotId
        - source
        - executedPlan
        - provenance
        - media
        - credits
        - referenceExpiresAt
        - cacheHit
      properties:
        success:
          const: true
        type:
          const: transform
        assetId:
          type: string
          pattern: ^ast_
        screenshotId:
          type: string
          pattern: ^scr_
        source:
          type: object
          required:
            - type
            - bounds
          properties:
            type:
              enum:
                - screenshot
                - region
                - part
                - stored-part
                - bounds
            regionId:
              type: string
            partId:
              type: string
            bounds:
              $ref: '#/components/schemas/TransformBounds'
        executedPlan:
          type: array
          minItems: 1
          maxItems: 4
          items:
            type: object
            description: 'One executed step: `resize` or `format`.'
          description: >-
            Ordered crop/resize and optional encoding. Format may occur once,
            last. Otherwise retain source format, lossy quality 80.
        provenance:
          type: object
          required:
            - sourceSha256
            - sourceBounds
            - engine
            - outputSha256
          properties:
            sourceSha256:
              type: string
              pattern: ^[a-f0-9]{64}$
            sourceBounds:
              $ref: '#/components/schemas/TransformBounds'
            sourceKind:
              const: stored-part
            parentScreenshotSha256:
              type: string
              pattern: ^[a-f0-9]{64}$
            sourceCoordinateSpace:
              const: screenshot
            operationCoordinateSpace:
              const: stored-part
            outputSha256:
              type: string
              pattern: ^[a-f0-9]{64}$
            engine:
              type: object
              properties:
                backend:
                  const: browser-v1
                version:
                  const: screenshot-transform-v1
                browser:
                  type: string
                codec:
                  const: canvas-2d-v1
              required:
                - backend
                - version
                - browser
                - codec
              additionalProperties: false
        media:
          $ref: '#/components/schemas/TransformMedia'
        credits:
          $ref: '#/components/schemas/Credits'
        referenceExpiresAt:
          type: string
          format: date-time
        cacheHit:
          type: boolean
    TransformBatch:
      type: object
      required:
        - success
        - type
        - screenshotId
        - results
        - credits
      properties:
        success:
          type: boolean
        type:
          const: transform
        screenshotId:
          type: string
        results:
          type: array
          minItems: 1
          maxItems: 16
          items:
            allOf:
              - oneOf:
                  - $ref: '#/components/schemas/TransformSuccess'
                  - $ref: '#/components/schemas/Error'
              - type: object
                required:
                  - index
                properties:
                  index:
                    type: integer
                    minimum: 0
                    maximum: 15
        credits:
          $ref: '#/components/schemas/Credits'
        error:
          type: string
        code:
          type: string
        retryable:
          type: boolean
    Error:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          const: false
        error:
          type: string
        type:
          type: string
        code:
          type: string
        retryable:
          type: boolean
        fields:
          type: array
          items:
            $ref: '#/components/schemas/ErrorField'
        credits:
          $ref: '#/components/schemas/Credits'
    TransformPreset:
      type: string
      enum:
        - og-image
        - twitter-card
        - thumbnail
        - square
        - full-width
      description: >-
        Named output shape. Explicit fields override it.


        | preset | size | fit | format |

        |---|---|---|---|

        | `og-image` | 1200 × 630 | cover | webp 80 |

        | `twitter-card` | 1200 × 600 | cover | webp 80 |

        | `thumbnail` | 400 wide | contain | webp 75 |

        | `square` | 1080 × 1080 | cover | jpeg 85 |

        | `full-width` | 1920 wide | contain | webp 80 |


        On a whole full-page screenshot, the fixed-shape presets (`og-image`,
        `twitter-card`, `square`) take the **top** of the page at their aspect
        ratio. Set `region`, `bounds` or `fit` to choose differently.
    TransformBounds:
      type: object
      properties:
        x:
          type: integer
          minimum: 0
        'y':
          type: integer
          minimum: 0
        width:
          type: integer
          minimum: 1
          maximum: 32768
        height:
          type: integer
          minimum: 1
          maximum: 32768
      required:
        - x
        - 'y'
        - width
        - height
      additionalProperties: false
    TransformOutputSpec:
      type: object
      additionalProperties: false
      properties:
        preset:
          $ref: '#/components/schemas/TransformPreset'
        region:
          type: string
          description: >-
            Cut this saved region (e.g. `region-1`) from the screenshot.
            Requires a capture with `regions=true`.
        part:
          oneOf:
            - type: string
            - type: integer
              minimum: 0
          description: >-
            With `region`: one part of it, by id (`region-1-part-0`) or index
            (`0` = first). Original screenshot pixels.
        savedCrop:
          type: boolean
          default: false
          description: >-
            With `region` + `part`: use the stored crop file instead of cutting
            original pixels. Use it for pages whose whole screenshot exceeds 64
            MP or 24 MiB.
        bounds:
          oneOf:
            - type: string
              pattern: ^\d+,\d+,\d+,\d+$
              description: '`x,y,width,height` in screenshot pixels'
            - $ref: '#/components/schemas/TransformBounds'
          description: Cut an arbitrary rectangle. Cannot be combined with `region`.
        width:
          type: integer
          minimum: 1
          maximum: 32768
          description: Output width. Give one side to keep the aspect ratio.
        height:
          type: integer
          minimum: 1
          maximum: 32768
          description: Output height.
        fit:
          type: string
          enum:
            - contain
            - cover
            - fill
          description: >-
            How to fit both sides: `contain` may pad, `cover` may crop, `fill`
            may stretch. Default `contain`.
        background:
          type: string
          pattern: ^#[0-9a-fA-F]{6}$
          description: Padding / transparency fill for `contain` and JPEG.
        format:
          type: string
          enum:
            - jpeg
            - png
            - webp
          description: 'Output encoding. Default: same family as the source.'
        quality:
          type: integer
          minimum: 1
          maximum: 100
          description: Lossy quality. Default 80 (or the preset's).
      description: >-
        One output: optionally what to cut (`region` / `part` / `bounds`), then
        its size and encoding. No selector = the whole screenshot; no size =
        re-encode only.
    TransformMedia:
      type: object
      required:
        - stored
        - url
        - contentType
        - width
        - height
        - bytes
        - sha256
      properties:
        stored:
          type: boolean
          const: true
        url:
          type: string
          format: uri
          description: Direct B2, S3, or R2 URL.
        key:
          type: string
        location:
          type: string
        contentType:
          type: string
          enum:
            - image/jpeg
            - image/png
            - image/webp
        width:
          type: integer
        height:
          type: integer
        bytes:
          type: integer
        sha256:
          type: string
          pattern: ^[a-f0-9]{64}$
      additionalProperties: true
    Credits:
      type: object
      required:
        - charged
        - refunded
        - net
      properties:
        charged:
          type: integer
          minimum: 0
        refunded:
          type: integer
          minimum: 0
        net:
          type: integer
          minimum: 0
    AsyncAccepted:
      type: object
      required:
        - success
        - id
        - statusUrl
      properties:
        success:
          type: boolean
          const: true
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
            - screenshot
            - diff
            - transform
        statusUrl:
          type: string
        message:
          type: string
        credits:
          $ref: '#/components/schemas/Credits'
    ErrorField:
      type: object
      required:
        - field
        - message
      properties:
        field:
          type: string
        message:
          type: string
  responses:
    AsyncAccepted:
      description: Job accepted
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AsyncAccepted'
    Unauthorized:
      description: Invalid or missing Scrinly key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: Credit allowance exhausted
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Unknown or cross-account resource
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooLarge:
      description: Request or image exceeds limits
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Scrinly API key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.