Skip to main content
SudoMock turns a Photoshop template into a finished product image over HTTP, so the Http client that already ships with Laravel is enough. This page stores the key in config/services.php, uploads a template once from an Artisan command, and renders it from a route.

Hand this to your coding agent to add SudoMock to a Laravel app.

Open in Cursor

Prerequisites

  • A Laravel application. The Http client is part of the framework, so there is nothing to install.
  • An API key from API keys. Keys start with sm_ and travel in the x-api-key header. See Authentication.
  • A PSD with at least one smart object, reachable at a public URL. See Preparing a PSD.
  • The Quickstart if you want to see the same two calls without a framework around them.

Guide

1

Store the key and configure the client

Put the key in the environment file and read it through a config entry. The two UUIDs stay empty until the next step fills them.
One macro gives every call the same base URL, header and timeout:
app/Providers/AppServiceProvider.php
Read the key with config() rather than env(). Once the config is cached in production, env() answers null and every request comes back 401.
2

Upload a PSD once

The upload reports the layers it found, each with the UUID you address it by. An Artisan command keeps that out of the request path.
app/Console/Commands/ImportMockup.php
Copy the printed mockup UUID and the UUID of the smart object you want to fill into .env. A template is uploaded once and rendered as often as you like.
3

Render from a route

The render takes the artwork URL from the request and answers with the finished image. On Laravel 11 and newer, php artisan install:api creates routes/api.php if the application does not have one yet.
routes/api.php
app/Http/Controllers/RenderMockupController.php
fit decides how the artwork meets the layer, and a color block on the same entry recolours it. See Fit and blend modes.
This render holds the HTTP request open until the image exists. Send is_async as true instead and the call answers at once with a job_id you read from GET /api/v1/jobs/{job_id}, or receive on a webhook. That is the shape to pair with a queued job.
4

Handle a failed call

Replace throw() with a branch once the route is live. $payload is the body from the previous step. Retry the statuses that are worth repeating, and leave the rest alone.
A 422 means the body is wrong, so repeating it repeats the failure. A 429 carries Retry-After, which you can read from $response->header('Retry-After') and sleep for exactly that long. Every code and its remedy is on Errors.

Next steps

Errors

Every error_code, and which statuses are worth retrying.

Webhooks

Let a queued render call your app back instead of polling it.

Text layers

Replace copy in the same template without a second upload.

Usage limits

The request rate and the parallel ceiling a key works inside.