> ## Documentation Index
> Fetch the complete documentation index at: https://docs.makelocalads.com/llms.txt
> Use this file to discover all available pages before exploring further.

# From a template

> Build a reusable shot pack, then generate a photoshoot from it.

[← Photoshoots](/photoshoots/overview)

**Endpoints:** `POST https://makelocalads.com/api/v1/templates` and
`POST https://makelocalads.com/api/v1/photoshoots`
**Permissions:** create with `photoshoots:generate`, list with
`photoshoots:read`

A template is a reusable pack of shots, saved from 1 to 24 images. Each shot
keeps the same aspect ratio as its source image, and LocalAds retains a stable
copy of every accepted image, so the template does not depend on the original
URLs remaining available.

## Usage notes

* Create the template from the product's `product_id`: it decides which brand
  can use the template. A template shoot requires the product and template to
  belong to the same brand.
* Do not include `quick_shoot` or `moodboard_shots` when you send
  `template_id`.
* Each rendered image costs 1 credit; creating and listing templates is free.

## Create a template

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  --url https://makelocalads.com/api/v1/templates \
  --header "Authorization: Bearer $LOCALADS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
    "title": "Studio essentials",
    "image_urls": [
      "https://assets.makelocalads.com/public-api/example/images/template-shot-1.jpg",
      "https://assets.makelocalads.com/public-api/example/images/template-shot-2.jpg"
    ]
  }'
```

The response returns `201 Created` and the template, with one `shots` entry
per image, in order:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "photoshoot-shot-pack:38b66c64-e76f-483f-a185-2d377ee2c5eb",
  "brand_id": "1423f915-beca-4c53-a5e4-7c8c99537be9",
  "title": "Studio essentials",
  "preview_url": "https://cdn.example.com/template-preview.jpg",
  "shot_count": 4,
  "shots": [
    {
      "id": "6d8f2a4c-9b1e-4f7a-8c3d-2e5f7a9b1c4d",
      "position": 1,
      "preview_url": "https://cdn.example.com/template-shot-1.jpg",
      "width": 1400,
      "height": 1050,
      "description": null
    }
  ],
  "created_at": "2026-07-20T10:00:00.000Z",
  "updated_at": "2026-07-20T10:05:00.000Z"
}
```

### Template fields

<ParamField body="product_id" type="string" required>
  The product the template is made for. It decides which brand can use the
  template.
</ParamField>

<ParamField body="title" type="string" required>
  The template's name, 1 to 160 characters.
</ParamField>

<ParamField body="image_urls" type="array" required>
  1 to 24 HTTPS images for the pack. LocalAds keeps a stable copy of each
  accepted image and preserves its aspect ratio.
</ParamField>

An image that is only on your computer can be uploaded first with
[Image uploads](/setup/image-uploads).

## List and retrieve templates

List the templates available to a brand, newest first:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://makelocalads.com/api/v1/templates?brand_id=1423f915-beca-4c53-a5e4-7c8c99537be9&limit=20" \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

Retrieve one template to inspect its ordered shots and output dimensions:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://makelocalads.com/api/v1/templates/photoshoot-shot-pack%3A38b66c64-e76f-483f-a185-2d377ee2c5eb" \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

A template `id` contains a colon; URL-encode it in the path, as above.

## Start the photoshoot

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST \
    --url https://makelocalads.com/api/v1/photoshoots \
    --header "Authorization: Bearer $LOCALADS_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{
      "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
      "template_id": "photoshoot-shot-pack:38b66c64-e76f-483f-a185-2d377ee2c5eb"
    }'
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const response = await fetch("https://makelocalads.com/api/v1/photoshoots", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LOCALADS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      product_id: "a1516a75-9be5-461a-93af-c1896a0a3127",
      template_id:
        "photoshoot-shot-pack:38b66c64-e76f-483f-a185-2d377ee2c5eb",
    }),
  });

  const photoshoot = await response.json();
  ```

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

  response = requests.post(
      "https://makelocalads.com/api/v1/photoshoots",
      headers={
          "Authorization": f"Bearer {os.environ['LOCALADS_API_KEY']}",
          "Content-Type": "application/json",
      },
      json={
          "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
          "template_id": "photoshoot-shot-pack:38b66c64-e76f-483f-a185-2d377ee2c5eb",
      },
  )

  photoshoot = response.json()
  ```
</CodeGroup>

The response is `202 Accepted` with the photoshoot: one `outputs` entry per
template shot, all `queued`, and `template_id` set. `max_credits` (minimum 1)
refuses the request with `422 credit_limit_exceeded`, before anything is
charged, if the shoot would cost more than this many credits.

Poll it like any other photoshoot: see
[Get the photos](/photoshoots/results).

## Errors

| Status | Code | Meaning |
| - | - | - |
| 404 | `product_not_found`, `template_not_found` | Not available to you |
| 422 | `template_brand_mismatch` | The product and template must belong to the same brand |
| 422 | `plan_required` | The organization's plan does not allow photoshoot generation |

See [Errors and retries](/concepts/errors) for the error format.
