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

> ## Agent Instructions
> The base URL is https://api.sudomock.com.
> Authenticate every request with the x-api-key header. Keys begin with sm_.
> A render returns the finished image at data.print_files[0].export_path. A request sent with is_async true returns a job_id to poll at GET /api/v1/jobs/{job_id}.
> Prefer the official SDKs over hand-written HTTP calls: npm install sudomock for Node, pip install sudomock for Python.

# Render product mockups with Flask

> Upload a PSD once, then render it from a Flask route.

<Prompt description="Hand this to your coding agent before it writes a SudoMock call." icon="microchip" iconType="solid" actions={["copy", "cursor"]}>
  # Render mockups with the SudoMock Python SDK

  **Purpose:** enforce the current and correct way to render SudoMock mockups
  from a Python application.
  **Scope:** all AI-generated code or advice about SudoMock in this project must
  follow these rules.

  ***

  ## 1. Setup

  The human creates an API key at
  [https://sudomock.com/dashboard/api-keys](https://sudomock.com/dashboard/api-keys).
  Keys begin with `sm_` and live in the `SUDOMOCK_API_KEY` environment
  variable, never in source code.

  Install the SDK with the project's existing package manager. It runs on
  Python 3.9 and newer.

  ```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
  pip install sudomock
  ```

  Build the client once, at module level, and reuse it.

  ```python theme={"theme":{"light":"github-light","dark":"vesper"}}
  import os

  from sudomock import SudoMock

  client = SudoMock(api_key=os.environ["SUDOMOCK_API_KEY"])
  ```

  `api_key` is keyword only. With no argument the client reads
  `SUDOMOCK_API_KEY` itself.

  ***

  ## 2. The two calls

  Upload a PSD once, outside the request path:

  ```python theme={"theme":{"light":"github-light","dark":"vesper"}}
  mockup = client.psd.upload(
      url="https://example.com/tshirt-mockup.psd",
      name="T-shirt front",
  )
  print(mockup.uuid, mockup.smart_objects[0].uuid)
  ```

  Render it on every request:

  ```python theme={"theme":{"light":"github-light","dark":"vesper"}}
  render = client.renders.create(
      mockup_uuid=mockup_uuid,
      smart_objects=[
          {
              "uuid": smart_object_uuid,
              "asset": {"url": artwork_url, "fit": "crop"},
          }
      ],
      export_options={"image_format": "webp", "image_size": 1920},
  )
  print(render.url)
  ```

  ***

  ## 3. The contract underneath

  Base URL `https://api.sudomock.com`. Every request carries the key in the
  `x-api-key` header.

  * Upload a PSD: `POST /api/v1/psd/upload`, body `psd_file_url` and optional
    `psd_name`.
  * Render: `POST /api/v1/renders`, body `mockup_uuid` plus `smart_objects`
    or `text_layers`.
  * Check a key: `GET /api/v1/me`.
  * Poll an async job: `GET /api/v1/jobs/{job_id}`.

  A render answers with one entry per smart object it filled. Through the SDK
  that list is `render.print_files` and `render.url` is its first entry. Over
  raw HTTP the finished image is at `data.print_files[0].export_path`.

  ***

  ## 4. ALWAYS DO

  1. **Read the key from `SUDOMOCK_API_KEY`.** Never write it into a file that
     is committed.
  2. **Send it as `x-api-key`** when writing raw HTTP.
  3. **Upload the PSD once** and store `mockup_uuid` and the smart object UUID.
     Rendering does not need another upload.
  4. **Send `smart_objects` or `text_layers`.** A body with `mockup_uuid` alone
     renders the template untouched.
  5. **Catch `SudoMockError`** and read `status_code` and `error_code` from it.
     Its subclasses include `AuthenticationError`, `InsufficientCreditsError`,
     `NotFoundError`, `ValidationError`, `RateLimitError` and `ServerError`.
  6. **Pass `is_async=True` for a long render**, then poll with
     `client.jobs.wait(job_id)` or receive a webhook.

  ***

  ## 5. NEVER DO

  1. **Do not** hardcode a key, and do not ship one to a browser.
  2. **Do not** send `Authorization: Bearer`. SudoMock reads `x-api-key` and
     nothing else.
  3. **Do not** invent a field. `client.renders.create` takes `mockup_uuid`,
     `smart_objects`, `text_layers`, `export_options`, `export_label` and
     `is_async`. Over raw HTTP the body also carries `group_layers`.
  4. **Do not** reach for a second SudoMock package. On PyPI the name is
     `sudomock`.
  5. **Do not** guess where the image is. Read `render.url`, or
     `data.print_files[0].export_path` over raw HTTP.
  6. **Do not** pass the key positionally. `SudoMock("sm_...")` raises.

  ***

  ## 6. Verification steps

  Before returning a SudoMock answer, check:

  1. Is the key read from the environment and sent as `x-api-key`?
  2. Does every path match section 3 exactly?
  3. Is every field name one listed in section 3 or section 5?
  4. Is `SudoMockError` handled?

  If a check fails, stop and revise until it passes.

  The full contract is at
  [https://assets.sudomock.com/openapi.json](https://assets.sudomock.com/openapi.json).
</Prompt>

## Prerequisites

Before you start, you will need:

* An [API key](/docs/dashboard/api-keys), beginning with `sm_`
* A PSD with a smart object, [prepared for rendering](/docs/psd-mockups/preparing-a-psd)

## Guide

<Steps>
  <Step title="Install">
    Add the SDK to a Flask 2.2 or newer project, then put the key in the
    environment as `SUDOMOCK_API_KEY`.

    <CodeGroup>
      ```bash pip theme={"theme":{"light":"github-light","dark":"vesper"}}
      pip install sudomock
      ```

      ```bash uv theme={"theme":{"light":"github-light","dark":"vesper"}}
      uv add sudomock
      ```

      ```bash poetry theme={"theme":{"light":"github-light","dark":"vesper"}}
      poetry add sudomock
      ```
    </CodeGroup>
  </Step>

  <Step title="Upload the mockup once">
    Run this outside the request path and keep the two UUIDs it prints, because
    [one upload](/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd) serves
    every render after it.

    ```python upload.py theme={"theme":{"light":"github-light","dark":"vesper"}}
    import os

    from sudomock import SudoMock

    client = SudoMock(api_key=os.environ["SUDOMOCK_API_KEY"])

    mockup = client.psd.upload(
        url="https://example.com/tshirt-mockup.psd",
        name="T-shirt front",
    )

    print("mockup:", mockup.uuid)
    for layer in mockup.smart_objects:
        print("smart object:", layer.name, layer.uuid)
    ```
  </Step>

  <Step title="Render from a route">
    The route places an artwork URL on the stored template, and
    [fit](/docs/concepts/fit-and-blend-modes) decides how that artwork meets the
    smart object area.

    ```python app.py theme={"theme":{"light":"github-light","dark":"vesper"}}
    import os

    from flask import Flask, jsonify, request
    from sudomock import SudoMock

    client = SudoMock(api_key=os.environ["SUDOMOCK_API_KEY"])

    MOCKUP_UUID = os.environ["SUDOMOCK_MOCKUP_UUID"]
    SMART_OBJECT_UUID = os.environ["SUDOMOCK_SMART_OBJECT_UUID"]

    app = Flask(__name__)


    @app.post("/render")
    def render_mockup():
        artwork_url = request.get_json()["artwork_url"]

        render = client.renders.create(
            mockup_uuid=MOCKUP_UUID,
            smart_objects=[
                {
                    "uuid": SMART_OBJECT_UUID,
                    "asset": {"url": artwork_url, "fit": "crop"},
                }
            ],
            export_options={
                "image_format": "webp",
                "image_size": 1920,
            },
        )

        return jsonify({"url": render.url})


    if __name__ == "__main__":
        app.run()
    ```
  </Step>
</Steps>

## Next steps

<CardGroup cols={3}>
  <Card title="Render a PSD mockup" icon="terminal" href="/docs/api-reference/psd-mockups/render-a-psd-mockup">
    Every field the render call takes, and the body it answers with.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/docs/errors">
    What a failed render answers, and which failures are worth retrying.
  </Card>

  <Card title="Webhooks" icon="bell" href="/docs/webhooks/overview">
    Let a long render call your app back instead of holding the request open.
  </Card>
</CardGroup>
