> ## 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 mockups in Cloudflare Workers

> Call the render API from a Worker with a stored API key.

<Prompt description="Build a Cloudflare Worker that renders SudoMock mockups." icon="sparkles" actions={["copy", "cursor"]}>
  Build a Cloudflare Worker that renders mockups with the SudoMock API.

  Setup

  * Create the project with `npm create cloudflare`.
  * Store the key as a Worker secret named `SUDOMOCK_API_KEY` using
    `npx wrangler secret put SUDOMOCK_API_KEY`, and put the same name in
    `.dev.vars` for local runs. Read it as `env.SUDOMOCK_API_KEY` inside
    `fetch(request, env)`. There is no `process.env` in this runtime.
  * The base URL is `https://api.sudomock.com`. Use the global `fetch` and
    the Web APIs the Workers runtime provides.

  Calls

  * Register a template once with `POST /api/v1/psd/upload` and a body of
    `{ "psd_file_url": "...", "psd_name": "..." }`. Keep `data.uuid` and
    `data.smart_objects[0].uuid` from the response.
  * Render with `POST /api/v1/renders` and a body of `mockup_uuid`,
    `smart_objects: [{ uuid, asset: { url, fit } }]`, and optionally
    `export_options: { image_format, image_size, quality }`.
  * `fit` is one of `fill`, `fit` or `crop`.
  * The finished image is at `data.print_files[0].export_path`.
  * Replace copy instead of artwork with a `text_layers` array, where each
    entry carries a text layer `uuid` and its new `text`.
  * For a long render, send `is_async` as `true`. The call answers `202`
    with a `job_id`, which you either poll at `GET /api/v1/jobs/{job_id}`
    or let a webhook hand to a second route on the same Worker.

  ALWAYS

  * Send the key in the `x-api-key` header. Keys begin with `sm_`.
  * Read the failure body and branch on `error_code`, keeping a default
    case that surfaces `message` and `details.suggestion`.
  * Retry `429`, `500` and `502` with backoff, and honour `Retry-After`.
    Never retry `400`, `401`, `402`, `404` or `422`.
  * Count the calls. Each one is a subrequest and Workers caps subrequests
    per invocation, so a queue of renders belongs behind `is_async` rather
    than in a loop inside one request.
  * Waiting on the API costs network time rather than compute time, so a
    long render does not press against the CPU ceiling. What it does hold
    is the caller's connection.

  NEVER

  * Never place the key in `wrangler.toml`, in client code, or in any file
    that is committed.
  * Always send the key in `x-api-key`. That is the header the API reads.
  * Never invent a field name. If it is not in the OpenAPI document at
    `https://assets.sudomock.com/openapi.json`, it does not exist.
  * Never read artwork from disk. Pass `asset.url`, or `asset.base64` with
    `content_type`.

  Verify

  * `GET https://api.sudomock.com/api/v1/me` with an `x-api-key` header
    answers with the account behind the key.
  * Run `npx wrangler dev`, POST to the render route, and confirm the
    answer carries an image URL.
</Prompt>

## Prerequisites

* An [API key](/docs/dashboard/api-keys), carried in the `x-api-key` header
  that [Authentication](/docs/authentication) describes.
* A PSD reachable over HTTPS, prepared as
  [Preparing a PSD](/docs/psd-mockups/preparing-a-psd) describes.
* A Cloudflare Worker with a bundling setup, from `npm create cloudflare`.

## Guide

<Steps>
  <Step title="Install">
    Create the project with C3, Cloudflare's generator, and choose the Hello
    World template.

    <CodeGroup>
      ```sh npm theme={"theme":{"light":"github-light","dark":"vesper"}}
      npm create cloudflare
      ```

      ```sh pnpm theme={"theme":{"light":"github-light","dark":"vesper"}}
      pnpm create cloudflare
      ```

      ```sh yarn theme={"theme":{"light":"github-light","dark":"vesper"}}
      yarn create cloudflare
      ```
    </CodeGroup>
  </Step>

  <Step title="Store the API key">
    A secret stays with the deployed Worker, so the key never reaches your
    repository. For `wrangler dev`, put the same name in a `.dev.vars` file
    you do not commit.

    ```sh theme={"theme":{"light":"github-light","dark":"vesper"}}
    npx wrangler secret put SUDOMOCK_API_KEY
    ```
  </Step>

  <Step title="Render from the Worker">
    The Worker reads the key from `env`, posts one render and hands back the
    finished image. Both UUIDs come from a single upload you run once, which
    [Create a mockup from a PSD](/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd)
    walks through, and neither of them is secret.

    ```javascript src/index.js theme={"theme":{"light":"github-light","dark":"vesper"}}
    const MOCKUP_UUID = "8f2c1d90-6b4e-4a37-9e55-0d1c7a3f21b8";
    const SMART_OBJECT = "b41a7e52-93c8-4d61-8f07-2ae5c9d04713";

    export default {
      async fetch(request, env) {
        const { artwork_url } = await request.json();

        const response = await fetch(
          "https://api.sudomock.com/api/v1/renders",
          {
            method: "POST",
            headers: {
              "x-api-key": env.SUDOMOCK_API_KEY,
              "content-type": "application/json",
            },
            body: JSON.stringify({
              mockup_uuid: MOCKUP_UUID,
              smart_objects: [
                {
                  uuid: SMART_OBJECT,
                  asset: { url: artwork_url, fit: "crop" },
                },
              ],
            }),
          },
        );

        const render = await response.json();

        if (!response.ok) {
          return Response.json(render, { status: response.status });
        }

        return Response.json({
          image: render.data.print_files[0].export_path,
        });
      },
    };
    ```

    A failure arrives as a JSON body carrying `error_code`, a readable
    `message` and a `details.suggestion`, which the Worker hands back with the
    status it arrived on. [Errors](/docs/errors) lists the codes.
  </Step>

  <Step title="Deploy">
    Deploy, then POST an artwork URL to the address wrangler prints. The
    answer carries the finished image.

    ```sh theme={"theme":{"light":"github-light","dark":"vesper"}}
    npx wrangler deploy
    ```
  </Step>
</Steps>

## Next steps

<CardGroup cols={3}>
  <Card title="Create a mockup from a PSD" icon="upload" href="/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd">
    The upload that hands you both UUIDs.
  </Card>

  <Card title="Render a PSD mockup" icon="play" href="/docs/api-reference/psd-mockups/render-a-psd-mockup">
    Every field the render body accepts.
  </Card>

  <Card title="Retrieve a single job" icon="clock" href="/docs/api-reference/jobs/retrieve-a-single-job">
    Poll a render sent with `is_async`.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/docs/errors">
    Every error code, and which are worth retrying.
  </Card>

  <Card title="Webhooks" icon="bell" href="/docs/webhooks/overview">
    Let a queued render call a second route.
  </Card>

  <Card title="Fit and blend modes" icon="image" href="/docs/concepts/fit-and-blend-modes">
    What `fit` and `blending_mode` change.
  </Card>
</CardGroup>
