---
title: "Update theme settings"
method: PATCH
path: "/projects/{projectId}/theme-settings"
tags: ["Theme settings"]
---

# Update theme settings

`PATCH /projects/{projectId}/theme-settings`

## Request

- Base URL: `https://studio.weaverse.io/api/v1/content`
- URL: `https://studio.weaverse.io/api/v1/content/projects/{projectId}/theme-settings`
- Auth: HTTP bearer

Shallow-merges `theme` over the project's own theme settings. Send only the top-level keys you want to change; `theme` must contain at least one key.

**Nested objects are replaced wholesale.** Fetch the current settings first when you need to preserve sibling values inside a nested object.

- Writes the project's own config only; a parent project's theme is never changed through a child project.
- `updatedKeys` lists the top-level keys whose stored value actually changed. It is empty for a no-op patch, in which case no cache invalidation happens.
- When values change, a theme version snapshot (restorable from Studio) is attempted before the write. Snapshots are deduplicated and best-effort, so do not assume every update has one.
- After a change, caches are invalidated like a Studio save so the edit goes live through api.weaverse.io. Propagation time is not guaranteed; read the settings again to verify.
- Clients or proxies that strip `PATCH` bodies may send `POST` to the same URL.

Concurrency: pass the `revision` from your GET as `expectedRevision`. If the settings changed since, the request returns `409 STALE_PROJECT` and writes nothing; re-read and reapply. Delegated (MCP OAuth) tokens must send it (`428 PRECONDITION_REQUIRED` otherwise); it is optional for API keys.

Required scope: `content:write`

## Path parameters

- `projectId` string, required

## Request body

- object
  - `theme` object, required: Top-level theme settings keys to merge. Must be a non-empty object.
  - `expectedRevision` string, date-time, nullable: Optimistic-concurrency token: the `revision` value from the GET read this patch was based on (`null` when GET returned null for a project whose revision was never set; compared null-safely). Required for delegated (MCP OAuth) tokens — omitted → 428 PRECONDITION_REQUIRED; optional for API keys. When the settings changed since (e.g. a concurrent studio save), the request is rejected with 409 STALE_PROJECT before anything is written. Re-read and reapply instead of retrying the same patch.

## Response `200`

Merge result with the full stored theme

- object
  - `object` 'theme_settings_update'
  - `projectId` string
  - `updatedKeys` string[]: Top-level keys whose stored value actually changed. Empty when the merge was a no-op (no cache invalidation happens in that case).
  - `theme` object: The full merged theme now stored on the project's own config.

## Other responses

- `400`: Invalid body (missing/empty `theme`, non-object body, or invalid JSON)
- `401`: Unauthorized
- `403`: Forbidden, or INSUFFICIENT_SCOPE: a delegated token without `content:write`
- `404`: Project not found
- `405`: Method not allowed (use PATCH or POST)
- `409`: STALE_PROJECT: the settings changed since the caller's read (expectedRevision mismatch — re-read and reapply)
- `428`: PRECONDITION_REQUIRED: a delegated (MCP OAuth) token omitted `expectedRevision`. API keys never receive this.

---

[API](https://skmtc.dev/weaverse/apis/weaverse-content-api.md) - [All operations](https://skmtc.dev/weaverse/apis/weaverse-content-api/llms.txt) - [OpenAPI document](https://skmtc.dev/weaverse/apis/weaverse-content-api/revisions/d189794169bb?raw)
