> For the complete documentation index, see [llms.txt](https://docs.scaleflex.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.scaleflex.com/developers-headless/studio/template-builder/render-url.md).

# The render URL

A saved template is a file in the DAM. Requesting its URL with a query string renders an image, and that is the whole interface - no SDK, no embed, no widget. This is what [Using a rendered template](/studio/template-builder/using-a-rendered-template.md) describes from the author's side.

```
https://<tenant>.filerobot.com/<path>/<template>.fdt?$layout=desktop&$headline=Autumn%20sale&force_format=png
```

{% hint style="warning" %}
**These parameter names are wire format.** `$layout`, `$layout_color`, `$locale`, `_apply_meta` and `force_format` are fixed identifiers, and a variable is addressed by its own slug. Existing names are never changed, so a URL you build today keeps rendering. Treat them as an API, not as an internal detail.
{% endhint %}

### Parameters

| Parameter       | Value                                             | What it does                                                                       |
| --------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `$layout`       | A layout slug                                     | Which layout to render. A template has at least one                                |
| `$layout_color` | A color                                           | The layout background. Ignored on the way *into* the editor - see the limits below |
| `$<slug>`       | Any value                                         | Sets the variable with that slug                                                   |
| `$locale`       | Comma-joined variant values, for example `fr,eur` | Selects regional variants                                                          |
| `_apply_meta`   | `1` or `0`                                        | Whether the renderer resolves metadata-sourced variables from the source asset     |
| `force_format`  | `png`                                             | Renders PNG rather than negotiating a format                                       |
| `vh`            | A cache key                                       | Present on a URL the DAM gave you. Leave it alone                                  |

Values are URL-encoded. A variable whose value is itself a URL carrying a query string has that query encoded into the value, so its `&` becomes `%26` and stays inside the variable rather than leaking into the outer query.

{% hint style="info" %}
**Three slugs are reserved.** A variable whose slug is `layout`, `locale` or `force_format` cannot be addressed by a query, because the name collides with a parameter. Choose another slug.
{% endhint %}

### Metadata automation changes which variables appear

A variable sourced from asset metadata is resolved by the renderer, not by you. With `_apply_meta=1` the renderer reads the source asset's metadata and picks the locale variant named by `$locale`.

So under automation those variables are **omitted from the query entirely**. An explicit `$slug=value` would override the resolution and pin an authoring-time value into the URL forever, which is exactly what automation is for avoiding.

With automation off, the author's typed values are the source of truth and are written explicitly. That is why the same template produces a longer query when metadata automation is switched off.

### Background-removed image sources

An image layer can be marked in the editor as requiring the *background-removed* version of whatever asset is substituted into it. In the document that is one optional attribute on the layer:

```xml
<image name="product" display="Product shot" file_uuid="..." bg_remove="1">
  $product_image$
</image>
```

It is written only when the requirement is on. `bg_remove="0"` is never written, so a template authored before the attribute existed does not gain it by being opened and saved.

The editor currently also puts the requirement into the URL. Every place a flagged layer's image source is written - the value of its `$<slug>`, the variable's own `default=`, and the source baked into a layer that has no variable - carries two delivery parameters:

| Parameter         | Value | What it does                                                |
| ----------------- | ----- | ----------------------------------------------------------- |
| `bg_remove`       | `1`   | Serve the asset's active background-removed derivative      |
| `fallback_master` | `0`   | Answer `404` rather than falling back to the original asset |

The image source is itself the *value* of a query parameter, so those two are encoded into that value: the inner `&` becomes `%26` and stays inside the variable instead of joining the outer query.

```
...template.fdt?$layout=desktop&$product=https%3A%2F%2F<tenant>.filerobot.com%2Fshot.jpg%3Fbg_remove%3D1%26fallback_master%3D0
```

Each key is emitted exactly once. A source that already carries either one has it replaced rather than duplicated, so a `templateQuery` that round-trips through the editor does not accumulate them.

{% hint style="warning" %}
**`fallback_master=0` fails the whole render.** An asset with no active background-removed derivative answers `404` for that image, and the template render answers `404` as a result. There is no partially composited output and no fallback to the original asset. That is the contract, not a limitation: a product rendered with its original background inside a template designed for a cut-out cannot be retracted, while a failed render can be fixed. The `404` is not cached, so the same URL starts returning an image as soon as a derivative exists.
{% endhint %}

If you build render URLs yourself, either leave the value the editor stored alone or append both parameters to the image sources of layers carrying `bg_remove="1"`.

{% hint style="info" %}
**Read the attribute, not the query.** This split of responsibility is transitional. A later release moves URL building to the render service, which reads `bg_remove` itself, and the editor stops appending the parameters. `bg_remove="1"` on the layer is the durable signal; matching on the two query parameters is not.
{% endhint %}

### `templateQuery`

When the widget saves, the `save` event carries a `templateQuery` next to the content. It is the query string that renders the template at its defaults - layout, variable values, locale - and you append it to the template's CDN URL when you want an image:

```
https://<tenant>.filerobot.com/<path>/<template>.fdt?<templateQuery>&force_format=png
```

**Persist it alongside `content`.** DAM-backed saves store it as file metadata. A [stateless](/developers-headless/studio/template-builder/storage-modes.md) host that drops it gets renders that fall back to whatever the XML alone implies.

**Pass it back in.** `load()` takes a `templateQuery`, so the query you stored reopens the template on the render it was left at:

```js
builder.load({ templateId, name, content, templateQuery })
```

Omit it and the editor falls back to the `default=` attributes in the XML - a different render whenever the query overrode any of them, which is the usual case for a template driven by metadata or by URL variables.

It is applied as display state, not as an edit. It selects the layout and fills variable values without marking the document dirty, so opening a template and closing it again is not an unsaved change. Entries naming a variable or layout the document no longer defines are ignored rather than treated as an error, so a stale query still opens the template.

### Known limits on the way in

All of these are cases where the editor's preview can differ from what the same query renders on the CDN. They apply to a query passed *into* the editor, not to one you request from the CDN.

* **`$locale` is not applied.** Regional selection is seeded from project information, which a guest embed never receives.
* **An explicitly empty value does not clear a variable.** The editor resolves `value || default`, so `$headline=` falls back to the XML default, while the renderer treats explicit-empty as empty. Omit the key instead of sending it blank.
* **Metadata-sourced variables behave differently by credential.** Under a [security template](/developers-headless/studio/template-builder/authentication.md) the query's value stands. Under a Hub session the editor re-resolves them from the linked asset shortly after load, overwriting it - that automation is the DAM's behavior and takes precedence.
* **`$layout_color` is ignored** and the layout's own color is used. A query the editor produced always agrees, so this only bites a hand-built one.

### Where to go next

* [Custom metadata](/developers-headless/studio/template-builder/custom-metadata.md) - filling variables from your own data
* [Storing a rendering copy](/developers-headless/studio/template-builder/storing-a-rendering-copy.md) - getting a CDN URL from a stateless save
