# Merchant theme

`GET|POST /functions/v1/merchant-theme-get`

Per-merchant appearance for the hosted surfaces: 22 tokens on capture, 19 on mandate, always fully populated, browser-safe to read.

```
GET  /functions/v1/merchant-theme-get?merchant_id=<id>&surface=capture|mandate
POST /functions/v1/merchant-theme-get   { "merchant_id": "<id>", "surface": "capture" }
```

Header `x-sidaxis-api-key`; a publishable `pk_` **is** accepted. `surface` defaults to `capture`. Responses are `cache-control: public, max-age=60`.

## Why a browser key is correct here

The response contains no key, no policy, no threshold and no subject data — nothing that changes a verdict — and the hosted surface must paint itself before any identity exists, so it can only carry a browser key. Nothing sensitive is reachable here even with a stolen `pk_`.

## Response

```
{
  "version": "theme.v1",
  "merchant_id": "mrc_9f2c",
  "surface": "capture",
  "customized": true,
  "tokens": ["brand_name", "mode", "color_primary", "..."],
  "theme": {
    "brand_name": "Northwind",
    "mode": "light",
    "color_primary": "212 78% 15%",
    "radius_md_px": 12,
    "font_body_family": "\"Inter\", sans-serif"
  }
}
```

## Tokens

22 tokens on `capture`; the `mandate` surface consumes the 19 that remain after dropping `logo_icon_url`, `color_success` and `color_danger`.

| Token | Type | Sidaxis default |
| --- | --- | --- |
| brand_name | text | Sidaxis |
| mode | light|dark | light |
| logo_wide_url, logo_square_url | url | null |
| logo_icon_url (capture only) | url | null |
| color_primary | H S% L% | 212 78% 15% |
| color_primary_fg | H S% L% | 0 0% 100% |
| color_background | H S% L% | 0 0% 97% |
| color_foreground | H S% L% | 212 78% 15% |
| color_muted | H S% L% | 210 10% 55% |
| color_border | H S% L% | 210 14% 89% |
| color_success (capture only) | H S% L% | 152 55% 42% |
| color_danger (capture only) | H S% L% | 0 84% 60% |
| radius_sm_px / radius_md_px / radius_lg_px | 0–64 | 8 / 12 / 20 |
| font_heading_family | text | "Space Grotesk", sans-serif |
| font_body_family | text | "Inter", sans-serif |
| font_heading_url / font_body_url | url (Google Fonts) | null |
| font_heading_weight / font_body_weight | 100–900 | 600 / 400 |

Colours are `H S% L%` triples, not hex.

## The default is never empty

A merchant with no row — or with any token left `NULL` — receives the fully populated Sidaxis theme, and `customized: false` says so explicitly. The surface never receives `NULL` and never needs a local fallback. Logos default to `null`, and the component is designed to render without one.

## Read-only by design

There is no endpoint to write a theme: themes are written in the developer console, as the authenticated merchant owner, and only published themes are served. A draft can sit unserved. If you are looking for a write API, it does not exist — do not build against one.

## Hosted mode applies the theme for you

When the buyer is on a Sidaxis-hosted screen, the screen fetches and applies the theme itself — do not call this endpoint in that flow. Call it only in headless/SDK integrations, where your own UI renders and you must apply the tokens yourself.

## White-label is included

White-label is **included** — no per-check cost, no add-on. Branding every hosted screen with the merchant's identity is part of the service.

## Errors

`400 MERCHANT_ID_REQUIRED`, `400 INVALID_SURFACE`, `401 INVALID_API_KEY`, `405 METHOD_NOT_ALLOWED`.
