> ## 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 with Google Sheets and SudoMock

> Render a mockup for every row of a sheet with Apps Script.

[Google Sheets](https://workspace.google.com/products/sheets) already holds the
list you want to render. Apps Script reads each row, calls the SudoMock render
API, and writes the finished image URL back into the same row. There is nothing
to install.

## Before you start

* An API key from your [dashboard](https://sudomock.com/dashboard/api-keys).
* A template. [Upload a PSD](/docs/psd-mockups/upload-a-psd) if you do not have one.
* Artwork at a URL that loads without signing in, because SudoMock fetches it.

Two ids go into every render: the mockup uuid, and the uuid of the layer you
fill. Both arrive together from
[List PSD mockups](/docs/api-reference/psd-mockups/retrieve-a-list-of-psd-mockups),
where each entry carries its own `uuid` and a `smart_objects` array holding the
`uuid` of every layer you can fill. That page has the call ready to copy in
eight languages. Read the pair once and keep it.

## Guide

<Steps>
  <Step title="Lay out the sheet">
    Put the header in row 1 and use four columns:

    | Column | Holds |
    | - | - |
    | A, Design URL | The artwork to place |
    | B, Product | Which template to render |
    | C, Mockup URL | Written back by the script |
    | D, Status | Written back by the script |
  </Step>

  <Step title="Store the key once">
    Open **Extensions, Apps Script**, then run this function a single time and
    delete it. The key lives in script properties from then on, so it never
    appears in the sheet or in a shared copy of the code.

    ```javascript theme={"theme":{"light":"github-light","dark":"vesper"}}
    function storeKey() {
      PropertiesService.getScriptProperties()
        .setProperty('SUDOMOCK_API_KEY', 'sm_your_api_key');
    }
    ```
  </Step>

  <Step title="Add the script">
    Paste this in place of the default code and save.

    ```javascript theme={"theme":{"light":"github-light","dark":"vesper"}}
    const API_URL = 'https://api.sudomock.com/api/v1/renders';

    const TEMPLATES = {
      't-shirt': {
        mockup: 'YOUR_MOCKUP_UUID',
        layer: 'YOUR_SMART_OBJECT_UUID',
      },
      mug: {
        mockup: 'YOUR_MOCKUP_UUID',
        layer: 'YOUR_SMART_OBJECT_UUID',
      },
    };

    function onOpen() {
      SpreadsheetApp.getUi()
        .createMenu('SudoMock')
        .addItem('Render every row', 'renderEveryRow')
        .addToUi();
    }

    function renderEveryRow() {
      const sheet = SpreadsheetApp.getActiveSpreadsheet()
        .getActiveSheet();
      const rows = sheet.getDataRange().getValues();

      for (let i = 1; i < rows.length; i++) {
        const [designUrl, product, existing] = rows[i];
        if (!designUrl || existing) continue;

        try {
          sheet
            .getRange(i + 1, 3)
            .setValue(renderOne(designUrl, product));
          sheet.getRange(i + 1, 4).setValue('Done');
        } catch (error) {
          sheet.getRange(i + 1, 4).setValue(error.message);
        }
        SpreadsheetApp.flush();
      }
    }

    function renderOne(designUrl, product) {
      const template = TEMPLATES[String(product).toLowerCase()];
      if (!template) {
        throw new Error('No template mapped for ' + product);
      }

      const response = UrlFetchApp.fetch(API_URL, {
        method: 'post',
        contentType: 'application/json',
        headers: {
          'x-api-key': PropertiesService.getScriptProperties()
            .getProperty('SUDOMOCK_API_KEY'),
        },
        payload: JSON.stringify({
          mockup_uuid: template.mockup,
          smart_objects: [
            {
              uuid: template.layer,
              asset: { url: designUrl, fit: 'crop' },
            },
          ],
          export_options: {
            image_format: 'webp',
            image_size: 1920,
            quality: 90,
          },
        }),
        muteHttpExceptions: true,
      });

      const result = JSON.parse(response.getContentText());
      if (response.getResponseCode() >= 400) {
        throw new Error(result.detail || 'Render failed');
      }
      return result.data.print_files[0].export_path;
    }
    ```
  </Step>

  <Step title="Run it from the sheet">
    Reload the spreadsheet. A **SudoMock** menu appears next to Help. Choose
    **Render every row** and grant the script permission the first time.

    <Note>
      The loop skips any row that already carries a URL in column C. Put
      `renderEveryRow` on a time driven trigger and each run picks up where the
      last one stopped, so a long list finishes across several runs without
      rendering anything twice.
    </Note>
  </Step>
</Steps>

## What comes back

The finished image URL arrives at `data.print_files[0].export_path`. That single
string is what lands in column C, and the whole response, field by field, is on
[Render a PSD mockup](/docs/api-reference/psd-mockups/render-a-psd-mockup).

## When a render fails

Column D holds the message the API returned. A `401` means the stored key is
missing or incomplete. A `422` names the field it rejected, usually an empty
cell reaching the API as an empty string. A download failure means the design
URL asks for a login. Every status is listed in
[Errors](/docs/errors).


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