
# Set the page template

`PUT /v1/branding/page-template` · permission `branding:write`

Writes the Liquid page template of the environment draft (Startup plan or higher, feature `branding_page_template`). The template must contain the `{{ idp.widget }}` slot exactly once; it is compiled in a sandbox on write. An empty string clears it.

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `If-Match` | header | yes | The `ETag` of the draft as last read (`"0"` if never written). Missing: `428`; stale: `412`. |
| `page_template` | string | yes | At most 102400 bytes; `""` clears. |

## Response

`200 OK` with the environment draft and its `ETag`. `theme` holds only the tokens that are set; unset tokens inherit from the platform default.

```json
{
  "theme": {"mode": "light", "light": {"color": {"primary": "#0a3d91"}}, "logo_height": 40},
  "css": null,
  "page_template": null,
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## Errors

| HTTP | code | When |
|---|---|---|
| 400 | `unknown_field` | The body has an unknown field or is not a JSON object. |
| 403 | `plan_feature_unavailable` | The plan does not include the feature (`plan_feature_unavailable`, extensions `feature` and `required_plan`). Development environments are not gated. |
| 412 | `precondition_failed` | `If-Match` does not match the current version of the draft. |
| 422 | `validation_failed` | `page_template` is missing or `null` (`required`), or too long (`too_long`). |
| 422 | `template_forbidden_construct` | The template uses a forbidden construct (for example `{% include %}` or `<script>`), or `{{ idp.widget }}` does not occur exactly once. |
| 428 | `precondition_required` | The `If-Match` header is missing. |
| 401 | `unauthenticated` | The credential is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The credential lacks the required permission. |
| 429 | `rate_limited` | The rate limit was exceeded; retry after the `Retry-After` period. |
| 503 | `service_unavailable` | A dependency is temporarily unavailable; retry after the `Retry-After` period. |

## Example

```bash
curl -X PUT https://api.eu.keenauth.com/v1/branding/page-template \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "3"' \
  -d '{"page_template":"<div class=\"shell\">{{ idp.widget }}</div>"}'
```
