> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/api-reference/pass-management/theme.md).

# Theme

## GET /api/{tenantId}/admin/themes

> List themes

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PassTheme":{"required":["id","name"],"type":"object","properties":{"id":{"maxLength":64,"minLength":0,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","type":"string","description":"Stable, URL-safe identifier (for example `summer-2026`): 1 to 64 letters, digits, `-` or\n`_`, starting with a letter or a digit, case-sensitive. Cannot be changed after creation:\npasses reference it and the theme's image paths contain it."},"name":{"maxLength":200,"minLength":0,"type":"string","description":"Display name shown in the admin interface and in the pass theme picker. At most 200 characters."},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels used to filter and group themes in the admin. At most 20 tags of 50 characters; each\nis trimmed, blank ones are dropped and a tag repeated in another casing is kept once."},"appearance":{"description":"Colors and images laid over the pass template. `null` keeps the pass template's appearance.","$ref":"#/components/schemas/PassThemeAppearance"},"content":{"description":"Localized text the pass template can show to the holder. `null` when the theme has none.","$ref":"#/components/schemas/PassThemeContent"},"creationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was created, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."},"lastModificationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was last created or updated, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."}},"additionalProperties":false,"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign."},"PassThemeAppearance":{"type":"object","properties":{"backgroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Card background color, as `#RRGGBB`, `#RGB` or `rgb(r, g, b)` with channels from 0 to\n255. Returned as `#RRGGBB` (`#FF7A52`). Empty keeps the pass template's color."},"foregroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field values. Same notations as `backgroundColor`; empty keeps the pass\ntemplate's color."},"labelColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field labels, on Apple Wallet only. Same notations as `backgroundColor`; empty keeps\nthe pass template's color."},"mainImageUrl":{"description":"The theme's default image. Used by the banner slot of each platform when it carries no override\nof its own — the Apple strip and the Google hero. Everything else, Apple's other large slots\nincluded, is only set through Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeAppearance.Images.","$ref":"#/components/schemas/PassThemeLocalizedString"},"images":{"type":"array","items":{"description":"One image override within a theme appearance.","$ref":"#/components/schemas/PassThemeImage"},"description":"Per-slot image overrides. At most one entry per Neo.Runtime.Wallet.Appearance.PassImageSlot: a second entry for the\nsame slot is refused with a 400."}},"additionalProperties":false,"description":"Colors and images of a theme. Anything left empty keeps the pass template's own value."},"PassThemeLocalizedString":{"required":["default"],"type":"object","properties":{"default":{"minLength":1,"type":"string","description":"Value used when no localization matches the requested language."},"localizations":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Per-language overrides keyed by language code (`fr`, `fr-FR`). Each key must be one of the\ntenant's available languages (compared ignoring case), or the theme is refused with a 400. For images\nthese are honoured on Apple Wallet only — Google Wallet takes a single image per slot and always\nreceives Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeLocalizedString.Default."}},"additionalProperties":false,"description":"A value with a mandatory default and optional per-language overrides."},"PassThemeImage":{"required":["slot","url"],"type":"object","properties":{"slot":{"description":"Which image on which platform this entry replaces.","$ref":"#/components/schemas/PassImageSlot"},"url":{"description":"Image location: an absolute `https://` URL, or the `path` of an image already uploaded to\nthis theme (`public/themes/summer-2026/apple-strip.png`). Anything else, including another\ntheme's image or one not uploaded yet, is refused with a 400.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"One image override within a theme appearance."},"PassImageSlot":{"enum":["appleStrip","appleArtwork","appleBackground","appleThumbnail","appleLogo","appleIcon","applePrimaryLogo","appleSecondaryLogo","googleHero","googleLogo","googleWideLogo","googleProgramLogo","googleWideProgramLogo","googleTitleImage","googleWideTitleImage"],"type":"string","description":"An image position on a wallet pass that an appearance source (a theme, for example) can override."},"PassThemeContent":{"type":"object","properties":{"title":{"description":"Theme title. Reachable from a pass template as `{{ theme.title }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"},"description":{"description":"Theme description. Reachable from a pass template as `{{ theme.description }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"Localized text of a theme, shown on the pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes":{"get":{"tags":["Theme"],"summary":"List themes","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Themes, in declaration order.","content":{"text/plain":{"schema":{"type":"array","items":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}},"application/json":{"schema":{"type":"array","items":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}},"text/json":{"schema":{"type":"array","items":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:read</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Create a theme

> The id must be unique and URL-safe: passes reference it and image paths are prefixed with it.\
> Images can only be uploaded once the theme exists, so a new theme uses \`https\://\` images or none; reference uploaded ones with an update.\
> Adds a configuration history entry and emits a theme created event.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PassTheme":{"required":["id","name"],"type":"object","properties":{"id":{"maxLength":64,"minLength":0,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","type":"string","description":"Stable, URL-safe identifier (for example `summer-2026`): 1 to 64 letters, digits, `-` or\n`_`, starting with a letter or a digit, case-sensitive. Cannot be changed after creation:\npasses reference it and the theme's image paths contain it."},"name":{"maxLength":200,"minLength":0,"type":"string","description":"Display name shown in the admin interface and in the pass theme picker. At most 200 characters."},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels used to filter and group themes in the admin. At most 20 tags of 50 characters; each\nis trimmed, blank ones are dropped and a tag repeated in another casing is kept once."},"appearance":{"description":"Colors and images laid over the pass template. `null` keeps the pass template's appearance.","$ref":"#/components/schemas/PassThemeAppearance"},"content":{"description":"Localized text the pass template can show to the holder. `null` when the theme has none.","$ref":"#/components/schemas/PassThemeContent"},"creationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was created, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."},"lastModificationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was last created or updated, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."}},"additionalProperties":false,"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign."},"PassThemeAppearance":{"type":"object","properties":{"backgroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Card background color, as `#RRGGBB`, `#RGB` or `rgb(r, g, b)` with channels from 0 to\n255. Returned as `#RRGGBB` (`#FF7A52`). Empty keeps the pass template's color."},"foregroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field values. Same notations as `backgroundColor`; empty keeps the pass\ntemplate's color."},"labelColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field labels, on Apple Wallet only. Same notations as `backgroundColor`; empty keeps\nthe pass template's color."},"mainImageUrl":{"description":"The theme's default image. Used by the banner slot of each platform when it carries no override\nof its own — the Apple strip and the Google hero. Everything else, Apple's other large slots\nincluded, is only set through Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeAppearance.Images.","$ref":"#/components/schemas/PassThemeLocalizedString"},"images":{"type":"array","items":{"description":"One image override within a theme appearance.","$ref":"#/components/schemas/PassThemeImage"},"description":"Per-slot image overrides. At most one entry per Neo.Runtime.Wallet.Appearance.PassImageSlot: a second entry for the\nsame slot is refused with a 400."}},"additionalProperties":false,"description":"Colors and images of a theme. Anything left empty keeps the pass template's own value."},"PassThemeLocalizedString":{"required":["default"],"type":"object","properties":{"default":{"minLength":1,"type":"string","description":"Value used when no localization matches the requested language."},"localizations":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Per-language overrides keyed by language code (`fr`, `fr-FR`). Each key must be one of the\ntenant's available languages (compared ignoring case), or the theme is refused with a 400. For images\nthese are honoured on Apple Wallet only — Google Wallet takes a single image per slot and always\nreceives Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeLocalizedString.Default."}},"additionalProperties":false,"description":"A value with a mandatory default and optional per-language overrides."},"PassThemeImage":{"required":["slot","url"],"type":"object","properties":{"slot":{"description":"Which image on which platform this entry replaces.","$ref":"#/components/schemas/PassImageSlot"},"url":{"description":"Image location: an absolute `https://` URL, or the `path` of an image already uploaded to\nthis theme (`public/themes/summer-2026/apple-strip.png`). Anything else, including another\ntheme's image or one not uploaded yet, is refused with a 400.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"One image override within a theme appearance."},"PassImageSlot":{"enum":["appleStrip","appleArtwork","appleBackground","appleThumbnail","appleLogo","appleIcon","applePrimaryLogo","appleSecondaryLogo","googleHero","googleLogo","googleWideLogo","googleProgramLogo","googleWideProgramLogo","googleTitleImage","googleWideTitleImage"],"type":"string","description":"An image position on a wallet pass that an appearance source (a theme, for example) can override."},"PassThemeContent":{"type":"object","properties":{"title":{"description":"Theme title. Reachable from a pass template as `{{ theme.title }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"},"description":{"description":"Theme description. Reachable from a pass template as `{{ theme.description }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"Localized text of a theme, shown on the pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes":{"post":{"tags":["Theme"],"summary":"Create a theme","description":"The id must be unique and URL-safe: passes reference it and image paths are prefixed with it.\nImages can only be uploaded once the theme exists, so a new theme uses `https://` images or none; reference uploaded ones with an update.\nAdds a configuration history entry and emits a theme created event.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Theme payload.","content":{"application/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"text/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"application/*+json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}},"required":true},"responses":{"201":{"description":"Theme created.","content":{"text/plain":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"application/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"text/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}}},"400":{"description":"Invalid payload or id; `errors` is keyed by JSON path.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:write</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"409":{"description":"Id already exists, or a concurrent theme write: reload and retry.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## GET /api/{tenantId}/admin/themes/{themeId}

> Get a theme

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PassTheme":{"required":["id","name"],"type":"object","properties":{"id":{"maxLength":64,"minLength":0,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","type":"string","description":"Stable, URL-safe identifier (for example `summer-2026`): 1 to 64 letters, digits, `-` or\n`_`, starting with a letter or a digit, case-sensitive. Cannot be changed after creation:\npasses reference it and the theme's image paths contain it."},"name":{"maxLength":200,"minLength":0,"type":"string","description":"Display name shown in the admin interface and in the pass theme picker. At most 200 characters."},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels used to filter and group themes in the admin. At most 20 tags of 50 characters; each\nis trimmed, blank ones are dropped and a tag repeated in another casing is kept once."},"appearance":{"description":"Colors and images laid over the pass template. `null` keeps the pass template's appearance.","$ref":"#/components/schemas/PassThemeAppearance"},"content":{"description":"Localized text the pass template can show to the holder. `null` when the theme has none.","$ref":"#/components/schemas/PassThemeContent"},"creationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was created, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."},"lastModificationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was last created or updated, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."}},"additionalProperties":false,"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign."},"PassThemeAppearance":{"type":"object","properties":{"backgroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Card background color, as `#RRGGBB`, `#RGB` or `rgb(r, g, b)` with channels from 0 to\n255. Returned as `#RRGGBB` (`#FF7A52`). Empty keeps the pass template's color."},"foregroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field values. Same notations as `backgroundColor`; empty keeps the pass\ntemplate's color."},"labelColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field labels, on Apple Wallet only. Same notations as `backgroundColor`; empty keeps\nthe pass template's color."},"mainImageUrl":{"description":"The theme's default image. Used by the banner slot of each platform when it carries no override\nof its own — the Apple strip and the Google hero. Everything else, Apple's other large slots\nincluded, is only set through Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeAppearance.Images.","$ref":"#/components/schemas/PassThemeLocalizedString"},"images":{"type":"array","items":{"description":"One image override within a theme appearance.","$ref":"#/components/schemas/PassThemeImage"},"description":"Per-slot image overrides. At most one entry per Neo.Runtime.Wallet.Appearance.PassImageSlot: a second entry for the\nsame slot is refused with a 400."}},"additionalProperties":false,"description":"Colors and images of a theme. Anything left empty keeps the pass template's own value."},"PassThemeLocalizedString":{"required":["default"],"type":"object","properties":{"default":{"minLength":1,"type":"string","description":"Value used when no localization matches the requested language."},"localizations":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Per-language overrides keyed by language code (`fr`, `fr-FR`). Each key must be one of the\ntenant's available languages (compared ignoring case), or the theme is refused with a 400. For images\nthese are honoured on Apple Wallet only — Google Wallet takes a single image per slot and always\nreceives Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeLocalizedString.Default."}},"additionalProperties":false,"description":"A value with a mandatory default and optional per-language overrides."},"PassThemeImage":{"required":["slot","url"],"type":"object","properties":{"slot":{"description":"Which image on which platform this entry replaces.","$ref":"#/components/schemas/PassImageSlot"},"url":{"description":"Image location: an absolute `https://` URL, or the `path` of an image already uploaded to\nthis theme (`public/themes/summer-2026/apple-strip.png`). Anything else, including another\ntheme's image or one not uploaded yet, is refused with a 400.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"One image override within a theme appearance."},"PassImageSlot":{"enum":["appleStrip","appleArtwork","appleBackground","appleThumbnail","appleLogo","appleIcon","applePrimaryLogo","appleSecondaryLogo","googleHero","googleLogo","googleWideLogo","googleProgramLogo","googleWideProgramLogo","googleTitleImage","googleWideTitleImage"],"type":"string","description":"An image position on a wallet pass that an appearance source (a theme, for example) can override."},"PassThemeContent":{"type":"object","properties":{"title":{"description":"Theme title. Reachable from a pass template as `{{ theme.title }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"},"description":{"description":"Theme description. Reachable from a pass template as `{{ theme.description }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"Localized text of a theme, shown on the pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}":{"get":{"tags":["Theme"],"summary":"Get a theme","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The theme.","content":{"text/plain":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"application/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"text/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:read</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Theme not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Update a theme

> Replaces the whole theme: omitted properties are cleared. The id is immutable. Creation date is kept; modification date is set by the server.\
> Uploaded images the saved theme no longer references (any language) are deleted.\
> Adds a configuration history entry and emits a theme updated event.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PassTheme":{"required":["id","name"],"type":"object","properties":{"id":{"maxLength":64,"minLength":0,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","type":"string","description":"Stable, URL-safe identifier (for example `summer-2026`): 1 to 64 letters, digits, `-` or\n`_`, starting with a letter or a digit, case-sensitive. Cannot be changed after creation:\npasses reference it and the theme's image paths contain it."},"name":{"maxLength":200,"minLength":0,"type":"string","description":"Display name shown in the admin interface and in the pass theme picker. At most 200 characters."},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels used to filter and group themes in the admin. At most 20 tags of 50 characters; each\nis trimmed, blank ones are dropped and a tag repeated in another casing is kept once."},"appearance":{"description":"Colors and images laid over the pass template. `null` keeps the pass template's appearance.","$ref":"#/components/schemas/PassThemeAppearance"},"content":{"description":"Localized text the pass template can show to the holder. `null` when the theme has none.","$ref":"#/components/schemas/PassThemeContent"},"creationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was created, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."},"lastModificationDate":{"oneOf":[{"type":"string","format":"date-time"},{"type":"integer","format":"int64"}],"description":"When the theme was last created or updated, in UTC. Set by the server; ignored on write. Accepts ISO 8601 date-time string or Unix epoch seconds in request inputs. Responses are serialized as ISO 8601 date-time strings."}},"additionalProperties":false,"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign."},"PassThemeAppearance":{"type":"object","properties":{"backgroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Card background color, as `#RRGGBB`, `#RGB` or `rgb(r, g, b)` with channels from 0 to\n255. Returned as `#RRGGBB` (`#FF7A52`). Empty keeps the pass template's color."},"foregroundColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field values. Same notations as `backgroundColor`; empty keeps the pass\ntemplate's color."},"labelColor":{"pattern":"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgb\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*\\))$","type":["null","string"],"description":"Color of the field labels, on Apple Wallet only. Same notations as `backgroundColor`; empty keeps\nthe pass template's color."},"mainImageUrl":{"description":"The theme's default image. Used by the banner slot of each platform when it carries no override\nof its own — the Apple strip and the Google hero. Everything else, Apple's other large slots\nincluded, is only set through Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeAppearance.Images.","$ref":"#/components/schemas/PassThemeLocalizedString"},"images":{"type":"array","items":{"description":"One image override within a theme appearance.","$ref":"#/components/schemas/PassThemeImage"},"description":"Per-slot image overrides. At most one entry per Neo.Runtime.Wallet.Appearance.PassImageSlot: a second entry for the\nsame slot is refused with a 400."}},"additionalProperties":false,"description":"Colors and images of a theme. Anything left empty keeps the pass template's own value."},"PassThemeLocalizedString":{"required":["default"],"type":"object","properties":{"default":{"minLength":1,"type":"string","description":"Value used when no localization matches the requested language."},"localizations":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Per-language overrides keyed by language code (`fr`, `fr-FR`). Each key must be one of the\ntenant's available languages (compared ignoring case), or the theme is refused with a 400. For images\nthese are honoured on Apple Wallet only — Google Wallet takes a single image per slot and always\nreceives Neo.Runtime.Theme.Catalog.IThemeCatalog.PassThemeLocalizedString.Default."}},"additionalProperties":false,"description":"A value with a mandatory default and optional per-language overrides."},"PassThemeImage":{"required":["slot","url"],"type":"object","properties":{"slot":{"description":"Which image on which platform this entry replaces.","$ref":"#/components/schemas/PassImageSlot"},"url":{"description":"Image location: an absolute `https://` URL, or the `path` of an image already uploaded to\nthis theme (`public/themes/summer-2026/apple-strip.png`). Anything else, including another\ntheme's image or one not uploaded yet, is refused with a 400.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"One image override within a theme appearance."},"PassImageSlot":{"enum":["appleStrip","appleArtwork","appleBackground","appleThumbnail","appleLogo","appleIcon","applePrimaryLogo","appleSecondaryLogo","googleHero","googleLogo","googleWideLogo","googleProgramLogo","googleWideProgramLogo","googleTitleImage","googleWideTitleImage"],"type":"string","description":"An image position on a wallet pass that an appearance source (a theme, for example) can override."},"PassThemeContent":{"type":"object","properties":{"title":{"description":"Theme title. Reachable from a pass template as `{{ theme.title }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"},"description":{"description":"Theme description. Reachable from a pass template as `{{ theme.description }}`.","$ref":"#/components/schemas/PassThemeLocalizedString"}},"additionalProperties":false,"description":"Localized text of a theme, shown on the pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}":{"put":{"tags":["Theme"],"summary":"Update a theme","description":"Replaces the whole theme: omitted properties are cleared. The id is immutable. Creation date is kept; modification date is set by the server.\nUploaded images the saved theme no longer references (any language) are deleted.\nAdds a configuration history entry and emits a theme updated event.","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Theme payload.","content":{"application/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"text/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"application/*+json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}},"required":true},"responses":{"200":{"description":"Theme updated.","content":{"text/plain":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"application/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}},"text/json":{"schema":{"description":"API representation of a theme — a reusable visual skin applied over a pass template for a season or\na campaign.","$ref":"#/components/schemas/PassTheme"}}}},"400":{"description":"Invalid payload, or body id differs from the route id; `errors` is keyed by JSON path.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:write</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Theme not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"409":{"description":"Concurrent theme write: reload and retry.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Delete a theme

> Also deletes its images. Refused while a pass or an enabled automation uses it; the refusal lists both. Emits a theme deleted event.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}},"ThemeInUse":{"required":["automations","passCount","themeId"],"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]},"themeId":{"type":"string","description":"Theme that was asked to be deleted."},"passCount":{"type":"integer","description":"Passes still using it.","format":"int32"},"automations":{"type":"array","items":{"description":"An automation that applies a theme.","$ref":"#/components/schemas/ThemeAutomation"},"description":"Enabled automations still applying it."}},"additionalProperties":{},"description":"Problem returned when deleting a theme that is in use."},"ThemeAutomation":{"type":"object","properties":{"id":{"type":"string","description":"Id of the automation, as used by the automation endpoints."},"displayName":{"type":["null","string"],"description":"Name shown in the admin, `null` when the automation has none."}},"additionalProperties":false,"description":"An automation that applies a theme."}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}":{"delete":{"tags":["Theme"],"summary":"Delete a theme","description":"Also deletes its images. Refused while a pass or an enabled automation uses it; the refusal lists both. Emits a theme deleted event.","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Theme and images deleted."},"400":{"description":"Invalid theme id.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:write</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Theme not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"409":{"description":"Theme in use (Theme in use problem), or a concurrent theme write.","content":{"text/plain":{"schema":{"description":"Problem returned when deleting a theme that is in use.","$ref":"#/components/schemas/ThemeInUse"}},"application/json":{"schema":{"description":"Problem returned when deleting a theme that is in use.","$ref":"#/components/schemas/ThemeInUse"}},"text/json":{"schema":{"description":"Problem returned when deleting a theme that is in use.","$ref":"#/components/schemas/ThemeInUse"}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## GET /api/{tenantId}/admin/themes/{themeId}/images

> List theme images

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ThemeImageInfo":{"type":"object","properties":{"path":{"type":"string","description":"Path of the image (`public/themes/{themeId}/{name}`), to put as is in an image location of this theme."},"name":{"type":"string","description":"File name of the image, the `fileName` of the image endpoints."}},"additionalProperties":false,"description":"Reference to an uploaded theme image."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}/images":{"get":{"tags":["Theme"],"summary":"List theme images","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Uploaded images, referenced or not.","content":{"text/plain":{"schema":{"type":"array","items":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}}},"application/json":{"schema":{"type":"array","items":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}}},"text/json":{"schema":{"type":"array","items":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:read</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Theme not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Upload a theme image

> The theme must exist. File name: starts with a letter or digit, then letters, digits, \`-\` or \`\_\`, ending \`.png\`, \`.jpg\` or \`.jpeg\`; the same name replaces the file.\
> Content must be a real PNG or JPEG matching the extension; metadata is stripped and the image re-compressed, never resized.\
> An image the theme does not reference is deleted by the next update.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ThemeImageInfo":{"type":"object","properties":{"path":{"type":"string","description":"Path of the image (`public/themes/{themeId}/{name}`), to put as is in an image location of this theme."},"name":{"type":"string","description":"File name of the image, the `fileName` of the image endpoints."}},"additionalProperties":false,"description":"Reference to an uploaded theme image."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}/images":{"post":{"tags":["Theme"],"summary":"Upload a theme image","description":"The theme must exist. File name: starts with a letter or digit, then letters, digits, `-` or `_`, ending `.png`, `.jpg` or `.jpeg`; the same name replaces the file.\nContent must be a real PNG or JPEG matching the extension; metadata is stripped and the image re-compressed, never resized.\nAn image the theme does not reference is deleted by the next update.","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","description":"Image file (multipart/form-data), at most 8 MB.","format":"binary"}}},"encoding":{"file":{"style":"form"}}}}},"responses":{"200":{"description":"Uploaded; returns the path to reference from the theme.","content":{"text/plain":{"schema":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}},"application/json":{"schema":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}},"text/json":{"schema":{"description":"Reference to an uploaded theme image.","$ref":"#/components/schemas/ThemeImageInfo"}}}},"400":{"description":"Missing or empty file, file over 8 MB, invalid name, or content not matching the extension.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:write</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Theme not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"409":{"description":"Concurrent theme write: retry."},"413":{"description":"The request body is larger than 8 MB.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## GET /api/{tenantId}/admin/themes/{themeId}/images/{fileName}

> Download a theme image

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}/images/{fileName}":{"get":{"tags":["Theme"],"summary":"Download a theme image","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"fileName","in":"path","description":"Image file name, e.g. `apple-strip.png`.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Image bytes, with the content type of the extension."},"400":{"description":"Invalid theme id or file name.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:read</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Image not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Delete a theme image

> A theme that still references the image is left with an empty slot.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/{themeId}/images/{fileName}":{"delete":{"tags":["Theme"],"summary":"Delete a theme image","description":"A theme that still references the image is left with an empty slot.","parameters":[{"name":"themeId","in":"path","description":"Theme id, case-sensitive.","required":true,"schema":{"type":"string"}},{"name":"fileName","in":"path","description":"Image file name, e.g. `apple-strip.png`.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Image deleted."},"400":{"description":"Invalid theme id or file name.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:write</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"Image not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Get theme usage

> One entry per theme: its pass count and the enabled automations applying it. A used theme cannot be deleted.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Theme"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.theme:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"ThemeUsage":{"type":"object","properties":{"themeId":{"type":"string","description":"Id of the theme this usage is about."},"passCount":{"type":"integer","description":"Number of passes currently pointing at it, `0` when none does.","format":"int32"},"automations":{"type":"array","items":{"description":"An automation that applies a theme.","$ref":"#/components/schemas/ThemeAutomation"},"description":"Enabled automations applying it to passes, empty when none does."}},"additionalProperties":false,"description":"What points at one theme."},"ThemeAutomation":{"type":"object","properties":{"id":{"type":"string","description":"Id of the automation, as used by the automation endpoints."},"displayName":{"type":["null","string"],"description":"Name shown in the admin, `null` when the automation has none."}},"additionalProperties":false,"description":"An automation that applies a theme."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/themes/usage":{"get":{"tags":["Theme"],"summary":"Get theme usage","description":"One entry per theme: its pass count and the enabled automations applying it. A used theme cannot be deleted.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Usage per theme.","content":{"text/plain":{"schema":{"type":"array","items":{"description":"What points at one theme.","$ref":"#/components/schemas/ThemeUsage"}}},"application/json":{"schema":{"type":"array","items":{"description":"What points at one theme.","$ref":"#/components/schemas/ThemeUsage"}}},"text/json":{"schema":{"type":"array","items":{"description":"What points at one theme.","$ref":"#/components/schemas/ThemeUsage"}}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.configuration.theme:read</c> scope.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.thewalletcrew.io/api-reference/pass-management/theme.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
