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

# How to edit PSD text by API

> Change the wording inside a Photoshop file over HTTP.

Yes, an API can edit the type inside a Photoshop file. SudoMock reads the live
text layers on upload and renders new wording, font, size and colour at request
time, without opening Photoshop and without flattening the design around the
text.

Use text overrides when you need to:

* **Personalise a run:** one template and a list of names, one finished image
  per name.
* **Localise a design:** the same layout, shipped in every language you sell
  in.
* **Correct a line without reopening the file:** a price, a date, a legal note.

Setup runs once per template. `POST /api/v1/psd/upload` returns the mockup
`uuid` and, under `text_layers`, one entry per live text layer with its own
`uuid` and its current wording, font, size and colour. Those two uuids are the
whole interface from here on, and
[Create a mockup from a PSD](/docs/api-reference/psd-mockups/create-a-mockup-from-a-psd)
carries the request and the full response.

## Send new wording with a render

Name the mockup, name the layer, send the text. The highlighted lines are the
whole of the change, and running the same call with the next name re-uploads
nothing.

<CodeGroup>
  ```js Node.js {9-14} 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": "Happy birthday, Jane"
        }
      ]
    }),
  });

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

  ```php PHP {6-11} 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": "Happy birthday, Jane"
      }
    ]
  }
  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-10} 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": "Happy birthday, Jane"
          }
      ]
  }

  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-17} 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": "Happy birthday, Jane"
        }
      ]
    }
  JSON

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

  puts response.body
  ```

  ```go Go {13-18} 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": "Happy birthday, Jane"
      }
    ]
  }`)

  	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-14} 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": "Happy birthday, Jane"
      }
    ]
  }
  """;

  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-12} 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": "Happy birthday, Jane"
      }
    ]
  }
  """;

  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-11} 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": "Happy birthday, Jane"
        }
      ]
    }'
  ```
</CodeGroup>

<Info>
  A render needs at least one entry across `smart_objects`, `text_layers` or
  `group_layers`, and a text-only personalisation satisfies that on its own.
  This request carries no artwork and no smart object.
</Info>

## Response format

The finished image is at `data.print_files[0].export_path`, and everything you
did not name renders as the designer drew it. `smart_object_uuid` comes back
empty here because the request placed no artwork.

```json Render response {6} theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "success": true,
  "data": {
    "print_files": [
      {
        "export_path": "https://cdn.sudomock.com/renders/c315f78f-d2c7-4541-b240-a9372842de94/render_8f2c1d4e.webp",
        "smart_object_uuid": ""
      }
    ]
  }
}
```

## Configuration

<ParamField body="mockup_uuid" type="string" required>
  The mockup the upload call returned. It selects the template this render
  draws from.
</ParamField>

<ParamField body="text_layers[].uuid" type="string" required>
  The layer you are changing, taken from the upload response. Layers you leave
  out keep their authored wording.
</ParamField>

<ParamField body="text_layers[].text" type="string">
  Replacement wording for a single-style layer. A layer that mixes styles takes
  `segments` instead and stays editable run by run. See
  [Text layers](/docs/text/text-layers).
</ParamField>

<ParamField body="text_layers[].font" type="string">
  A catalogue font `uuid` or its PostScript name, from the open-licensed
  catalogue or from a typeface you uploaded yourself. Omit it and the layer
  renders in the typeface the designer chose. See [Fonts](/docs/text/fonts).
</ParamField>

<ParamField body="text_layers[].font_size" type="number">
  Font size in pixels at the mockup's native resolution. Falls back to the
  layer's authored size.
</ParamField>

<ParamField body="text_layers[].color" type="string">
  Hex colour for the text you see, such as `#C0392B`. Falls back to the layer's
  authored colour.
</ParamField>

<ParamField body="text_layers[].fit" type="string" default="overflow">
  What happens when the replacement is wider than the layer's area:

  * `overflow`: the text keeps its size and may extend past the area.
  * `clip`: the text keeps its size and is cut to what fits.
  * `shrink`: the text is scaled down so it stays inside the area.

  [Fitting and colour](/docs/text/fitting-and-color) covers `vertical_align` and the
  outline arguments alongside it.
</ParamField>

## Limitations

Three fields in the upload response tell you what a template can do before you
build against it.

* `is_editable` false means the layer keeps its original appearance in this
  version. The reason and the workaround are in the support table on
  [Text layers](/docs/text/text-layers).
* `font_available` false means the layer's own typeface is not in your
  catalogue, so it falls back unless you set `font` yourself.
* `segment_count` above 1 means the layer mixes styles and takes `segments`
  rather than `text`.

## API reference

For the complete request and response contract, see the
[Render a PSD mockup](/docs/api-reference/psd-mockups/render-a-psd-mockup) API
reference.

<CardGroup cols={2}>
  <Card title="Text layers" icon="type" href="/docs/text/text-layers">
    The full override contract, warning codes and support table.
  </Card>

  <Card title="Fonts" icon="book-open" href="/docs/text/fonts">
    Browse the catalogue, or upload your brand typeface.
  </Card>
</CardGroup>
