> ## 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.

# Text layers

> Swap PSD wording, font, size and colour per render.

A text layer is live, editable type inside a PSD: a headline, a name, a price.
SudoMock reads those layers on upload and lets you change the wording, font,
size and colour at render time, so one template becomes a run of personalised
images.

Use text layers when you need to:

* **Personalise a run**: one template, a list of names, a finished image for each.
* **Localise a design**: the same layout carrying the wording each market reads.
* **Refresh a campaign**: a new price or date without reopening the file.

One template, an endless run of names. Each render swaps only the text.

<video alt="One PSD rendered three times, each with a different name set into the same text layer." src="https://mintcdn.com/sudo-mock/ArAl5hdr6a4aUNw3/images/demos/text-personalization.mp4?fit=max&auto=format&n=ArAl5hdr6a4aUNw3&q=85&s=45b9f6043e91d1ed89168f59479ec57a" poster="/images/demos/text-personalization-poster.jpg" controls muted loop playsInline data-path="images/demos/text-personalization.mp4" />

## Find the text layers

[Create a mockup from a PSD](/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd)
returns every live type layer it found under `text_layers`. Each entry carries
the layer's current values, the UUID you address it by, and the highlighted
signals that decide what your integration can change on it.

```json Upload response {8-13} theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
  "name": "Headline",
  "text_content": "YOUR BRAND",
  "font_postscript_name": "Poppins-Bold",
  "font_size": 96,
  "color": "#1A1A1A",
  "font_available": true,
  "is_editable": true,
  "segment_count": 1,
  "has_stroke_effect": true,
  "stroke_count": 2,
  "has_color_overlay": false,
  "enclosing_group_layers": [
    "9d7e4b18-3a65-4c21-8f90-2b6d7e1a5c43"
  ]
}
```

* `font_available`: false when the layer's own typeface is not in your
  catalogue, so the render falls back to a default. See [Fonts](/docs/text/fonts).
* `is_editable`: false when the layer keeps its original appearance in this
  version. The routes are under [Limitations](#limitations).
* `segment_count`: above 1 marks a layer that mixes styles, which takes
  `segments` rather than `text`.
* `has_stroke_effect` and `stroke_count`: the outlines the layer owns, which
  `stroke_color` recolours front to back.
* `has_color_overlay`: true when the visible colour comes from a colour effect
  rather than the fill.

[Fitting and colour](/docs/text/fitting-and-color) covers the last three in full.
The top-level `group_layers` array lists each editable enclosing outline group,
and appearing in that list is the editability signal for a group.

## Override text at render time

[Render a PSD mockup](/docs/api-reference/psd-mockups/render-a-psd-mockup) takes a
`text_layers` array naming only the layers you want to change. The highlighted
lines are the whole override: layers you leave out keep their authored wording,
so a template with six lines and one variable line takes a one-entry request.

<CodeGroup>
  ```js Node.js {9-17} theme={"theme":{"light":"github-light","dark":"vesper"}}
  const response = await fetch("https://api.sudomock.com/api/v1/renders", {
    method: "POST",
    headers: {
      "x-api-key": "sm_your_api_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
      "text_layers": [
        {
          "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
          "text": "SUMMER SALE",
          "font": "Poppins-Bold",
          "font_size": 96,
          "color": "#C0392B"
        }
      ]
    }),
  });

  const data = await response.json();
  console.log(data);
  ```

  ```php PHP {6-14} theme={"theme":{"light":"github-light","dark":"vesper"}}
  <?php

  $payload = <<<'JSON'
  {
    "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
    "text_layers": [
      {
        "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
        "text": "SUMMER SALE",
        "font": "Poppins-Bold",
        "font_size": 96,
        "color": "#C0392B"
      }
    ]
  }
  JSON;

  $ch = curl_init("https://api.sudomock.com/api/v1/renders");
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "x-api-key: sm_your_api_key",
      "Content-Type: application/json",
  ]);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);

  $response = curl_exec($ch);
  curl_close($ch);

  echo $response;
  ```

  ```python Python {5-13} theme={"theme":{"light":"github-light","dark":"vesper"}}
  import requests

  payload = {
      "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
      "text_layers": [
          {
              "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
              "text": "SUMMER SALE",
              "font": "Poppins-Bold",
              "font_size": 96,
              "color": "#C0392B"
          }
      ]
  }

  response = requests.post(
      "https://api.sudomock.com/api/v1/renders",
      headers={"x-api-key": "sm_your_api_key"},
      json=payload,
  )

  response.raise_for_status()
  print(response.json())
  ```

  ```ruby Ruby {12-20} theme={"theme":{"light":"github-light","dark":"vesper"}}
  require "net/http"
  require "uri"

  uri = URI("https://api.sudomock.com/api/v1/renders")
  request = Net::HTTP::Post.new(uri)
  request["x-api-key"] = "sm_your_api_key"
  request["Content-Type"] = "application/json"

  request.body = <<~JSON
    {
      "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
      "text_layers": [
        {
          "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
          "text": "SUMMER SALE",
          "font": "Poppins-Bold",
          "font_size": 96,
          "color": "#C0392B"
        }
      ]
    }
  JSON

  response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  puts response.body
  ```

  ```go Go {13-21} theme={"theme":{"light":"github-light","dark":"vesper"}}
  package main

  import (
  	"bytes"
  	"fmt"
  	"io"
  	"net/http"
  )

  func main() {
  	payload := []byte(`{
    "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
    "text_layers": [
      {
        "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
        "text": "SUMMER SALE",
        "font": "Poppins-Bold",
        "font_size": 96,
        "color": "#C0392B"
      }
    ]
  }`)

  	req, err := http.NewRequest("POST", "https://api.sudomock.com/api/v1/renders", bytes.NewBuffer(payload))
  	if err != nil {
  		panic(err)
  	}
  	req.Header.Set("x-api-key", "sm_your_api_key")
  	req.Header.Set("Content-Type", "application/json")

  	res, err := http.DefaultClient.Do(req)
  	if err != nil {
  		panic(err)
  	}
  	defer res.Body.Close()

  	body, _ := io.ReadAll(res.Body)
  	fmt.Println(string(body))
  }
  ```

  ```java Java {9-17} theme={"theme":{"light":"github-light","dark":"vesper"}}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  String payload = """
  {
    "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
    "text_layers": [
      {
        "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
        "text": "SUMMER SALE",
        "font": "Poppins-Bold",
        "font_size": 96,
        "color": "#C0392B"
      }
    ]
  }
  """;

  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.sudomock.com/api/v1/renders"))
      .header("x-api-key", "sm_your_api_key")
      .header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofString(payload))
      .build();

  HttpResponse<String> response = HttpClient.newHttpClient()
      .send(request, HttpResponse.BodyHandlers.ofString());

  System.out.println(response.body());
  ```

  ```csharp .NET {7-15} theme={"theme":{"light":"github-light","dark":"vesper"}}
  using System.Net.Http;
  using System.Text;

  var payload = """
  {
    "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
    "text_layers": [
      {
        "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
        "text": "SUMMER SALE",
        "font": "Poppins-Bold",
        "font_size": 96,
        "color": "#C0392B"
      }
    ]
  }
  """;

  var request = new HttpRequestMessage(HttpMethod.Post, "https://api.sudomock.com/api/v1/renders");
  request.Headers.Add("x-api-key", "sm_your_api_key");
  request.Content = new StringContent(payload, Encoding.UTF8, "application/json");

  var client = new HttpClient();
  var response = await client.SendAsync(request);

  Console.WriteLine(await response.Content.ReadAsStringAsync());
  ```

  ```bash cURL {6-14} theme={"theme":{"light":"github-light","dark":"vesper"}}
  curl -X POST "https://api.sudomock.com/api/v1/renders" \
    -H "x-api-key: sm_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "mockup_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",
      "text_layers": [
        {
          "uuid": "b7f2c1a0-9e34-4d21-8f0a-1c2b3d4e5f60",
          "text": "SUMMER SALE",
          "font": "Poppins-Bold",
          "font_size": 96,
          "color": "#C0392B"
        }
      ]
    }'
  ```
</CodeGroup>

Each entry in the array takes these fields.

<ParamField body="uuid" type="string" required>
  The text layer UUID from the upload response.
</ParamField>

<ParamField body="text" type="string">
  The replacement wording, 1 to 500 characters. Send this or `segments`, and
  exactly one of the two.
</ParamField>

<ParamField body="segments" type="array">
  Per-run overrides for a layer that mixes styles, sent instead of `text`.
  [Fitting and colour](/docs/text/fitting-and-color) carries the shape and the
  per-render caps.
</ParamField>

<ParamField body="font" type="string">
  A catalogue font `uuid`, or its PostScript name from
  [Retrieve a list of fonts](/docs/api-reference/fonts/retrieve-a-list-of-fonts).
  Defaults to the layer's authored typeface.
</ParamField>

<ParamField body="font_size" type="number">
  Type size in points. Defaults to the layer's authored size.
</ParamField>

<ParamField body="color" type="string">
  Hex colour for the type. Defaults to the layer's authored colour.
</ParamField>

`fit`, `vertical_align` and `stroke_color` ride on the same entry and are
covered in [Fitting and colour](/docs/text/fitting-and-color).

A render needs at least one entry across `smart_objects`, `text_layers` or
`group_layers`, which means a text-only personalisation renders without any
smart object at all.

<Tip>
  Targeting a hidden text layer renders it. Keep the optional lines, a name, a
  date, a discount, hidden in the PSD, and switch each one on only for the
  renders that need it. Leave the entry out and the layer stays hidden, exactly
  as it was in the source file.
</Tip>

## What renders

Point text, multi-line point text and paragraph, or box, text all render with
your wording, matched to the original font, size and colour. Manual line breaks
and leading render true to the original on every line. Area text wraps to its
box as designed, and text past the box renders clipped. Character styling
carries over as authored: faux bold and italic, underline and strikethrough,
letter spacing, all caps and small caps, superscript and subscript, baseline
shift, horizontal and vertical scale, the fill opacity the designer set, and
all five anti-alias settings. A layer that mixes styles stays editable run by run, and
each run keeps its own styling. Outlines, including stacked ones, render
faithfully and recolour individually.

Rotated text renders live at its exact angle, and ten warp styles render live
with your new wording: Arc, Arc Lower, Arc Upper, Arch, Bulge, Flag, Wave,
Fish, Rise and Squeeze. Latin, Cyrillic and Greek scripts, including Turkish
and accented characters, render exactly as designed. Arabic and Hebrew render
right to left, with a fallback that keeps them readable when the chosen font
lacks those glyphs. Every render draws on the built-in catalogue, and on your
own uploaded typefaces where you have them, which [Fonts](/docs/text/fonts) covers.

<Columns cols={2}>
  <video alt="The same sentence rendered through several Photoshop warp styles." src="https://mintcdn.com/sudo-mock/ArAl5hdr6a4aUNw3/images/demos/text-warp-styles.mp4?fit=max&auto=format&n=ArAl5hdr6a4aUNw3&q=85&s=8a811722b634f8f457bb33e4e057a0bb" poster="/images/demos/text-warp-styles-poster.jpg" controls muted loop playsInline data-path="images/demos/text-warp-styles.mp4" />

  <video alt="A rotated text layer keeping its angle while the words are replaced." src="https://mintcdn.com/sudo-mock/ArAl5hdr6a4aUNw3/images/demos/text-rotate-segments.mp4?fit=max&auto=format&n=ArAl5hdr6a4aUNw3&q=85&s=b064c6d37cb03861505a1fb5f7f16ece" poster="/images/demos/text-rotate-segments-poster.jpg" controls muted loop playsInline data-path="images/demos/text-rotate-segments.mp4" />
</Columns>

Text output is checked side by side against Photoshop reference exports, most
recently in July 2026. Validate each production template against a reference
export of its own before you ship it.

## Limitations

Some layers render with their original appearance rather than your wording.
Each one has a route:

* **Vertical text** is not editable in this version. Convert the layer to
  horizontal point text in Photoshop before upload.
* **Justified alignment** is not editable in this version. Switch the paragraph
  to left, centre or right alignment before upload.
* **CJK layout** is still being calibrated, so spacing may differ slightly. The
  families are in the catalogue. Rasterise the layer before upload, or render
  the text as an image placed in a smart object slot.
* **Warp styles outside the ten listed above** render as authored. Render your
  text as an image and place it in a warped smart object slot.
* **Rotation combined with mixed styles or a text box** renders with the
  layer's original appearance.
* **A multi-line edit that would overflow its slot** renders the original
  rather than breaking the composition. Shorten the replacement text, or design
  the slot with more room.

Optical kerning nuances and ligature toggles may differ slightly from the
original.

<Note>
  One render carries up to 50 `text_layers` entries, and one layer's text is
  capped at 500 characters.
</Note>

## Errors and warnings

An error blocks the render so you can fix the request. A warning rides along
with a successful render so you know what happened. Both codes are stable, so
your integration can branch on them rather than on message text.

These block the render:

* `TEXT_LAYER_NOT_FOUND`, 400: the text layer uuid in your request does not
  belong to this mockup.
* `FONT_NOT_FOUND`, 422: the font you explicitly requested is not in your
  catalogue. Upload it, pick a catalogue font, or omit `font`.
* `FONT_AMBIGUOUS`, 422: the font name matches more than one font available to
  you. The response carries a `candidates` list of uuids; send one as `font`.
* `TEXT_SEGMENTS_REQUIRED`, 422: this mixed-style layer needs `segments` rather
  than one `text` value.
* `SEGMENT_INDEX_OUT_OF_RANGE`, 422: the segment index is outside this layer's
  range. Read valid indexes from the upload response.
* `TEXT_SEGMENTS_UNSUPPORTED`, 422: this single-style layer needs one `text`
  value rather than segment overrides.
* `TEXT_SEGMENTS_LIMIT`, 422: the request carries more than 200 segment
  overrides across its text layers.
* `TEXT_TOO_LONG`, 422: the effective combined segment text is longer than 500
  characters for this layer.

These ride along with a successful render, each carrying only `code` and
`message`:

* `TEXT_FONT_FALLBACK`: the font for this layer was unavailable, so a default
  font was used. Upload the font or pick an available one, then render again.
* `TEXT_FONT_AMBIGUOUS`: the layer's font name matches more than one available
  font, so a default was used. Send an explicit `font` uuid for this layer.
* `TEXT_FONT_MISSING_GLYPHS`: the selected font lacks some characters in the
  replacement text. Choose a font that supports the text.
* `TEXT_WARP_BAKED`: the layer uses one of the remaining warp styles, so it
  rendered with its original appearance.
* `TEXT_LAYER_NOT_EDITABLE`: this layer uses a structure or style not editable
  in this version, so it kept its original appearance.
* `TEXT_FIT_SHRUNK`: you selected `shrink` and the text was scaled down to fit
  its area.
* `TEXT_OVERRIDE_NOT_APPLIED`: the change could not be applied to this layer,
  so it kept its original content.
* `TEXT_STROKE_NOT_PRESENT`: you sent `stroke_color` for a layer with no
  outline of its own. Use `group_layers` for an enclosing group outline.
* `TEXT_COLOR_HIDDEN_BY_EFFECT`: a gradient effect covers the text, so the
  requested colour may not be visible.

Every other status code is in [Errors](/docs/errors).

## API reference

For the complete field contract and a live playground, see
[Render a PSD mockup](/docs/api-reference/psd-mockups/render-a-psd-mockup) and
[Create a mockup from a PSD](/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd).

<CardGroup cols={2}>
  <Card title="Fitting and colour" icon="ruler" href="/docs/text/fitting-and-color">
    Keep replacement text inside its box, and recolour text and outlines.
  </Card>

  <Card title="Fonts" icon="type" href="/docs/text/fonts">
    The catalogue, custom uploads, and picking a font in a render.
  </Card>
</CardGroup>
