> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trylath.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Every Lath operation is POST https://platform.trylath.com/<operation name with dots replaced by slashes>, with a JSON body and `Authorization: Bearer <key>`. `email.send` is POST /email/send.
> Branch on `error.code`, never on `error.message`. Every refusal also carries `error.fix`, which names the next step.
> Send an `Idempotency-Key` header on any operation that is not retry-safe, so a retry cannot run it twice.
> A `lath_test_` key emails only the account's own members and sends no SMS; a `lath_live_` key reaches real recipients and is billed.
> The OpenAPI document, generated from the same registry as the routes, is at https://platform.trylath.com/openapi.json.

# Theming

> One set of colours, fonts and words, worn by the hosted sign-in pages and by the mail Lath sends for you. Twenty tokens, a few hundred strings, and a contrast check that refuses a palette nobody could read.

## A draft, then a publish

`account.theme.get` returns three things: what is published, what is drafted, and the defaults, along with the full list of token and copy keys — so a form can be built from the response rather than from a list copied into your own code.

`account.theme.set` saves a draft and may be called with a test key. `account.theme.publish` is what changes anything anybody sees, and `account.theme.reset` puts it back; both are refused from a test key with `live_only`, because both change what live users receive.

Send only what you are changing. A key you leave out keeps its value, and a key set to `null` drops your override so Lath's default shows through again — which is how you undo one colour without restating the other nineteen.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://platform.trylath.com/account/theme/set \
    -H "Authorization: Bearer $LATH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"tokens":{"action-primary-bg":"#1A2B3C","fg-on-action":"#FFFFFF"}}'
  ```

  ```ts TypeScript theme={null}
  import { createLath } from "@trylath/sdk";

  const lath = createLath({ key: process.env.LATH_API_KEY });

  const { result } = await lath.account.theme.set({
    tokens: {
      "action-primary-bg": "#1A2B3C",
      "fg-on-action": "#FFFFFF",
    },
  });
  ```

  ```bash CLI theme={null}
  lath account theme set \
    --tokens '{"action-primary-bg":"#1A2B3C","fg-on-action":"#FFFFFF"}'
  ```
</CodeGroup>

## One theme for the project, both environments

A theme belongs to the project, not to an environment: live and test share it. That is deliberate — a brand is not a per-environment setting — but it means a draft saved with a test key and then published is published for live users too.

The hosted pages read the published theme from a public endpoint keyed by environment id, which needs no key because a sign-in page is read by people who have not signed in yet. It answers with defaults filled in and is cached for a minute, so a publish takes up to that long to appear.

## What the tokens are

Twenty, all strings: backgrounds and surfaces, two border weights, three text tones, the primary action and its foreground, a focus ring, two danger colours, a UI font and a mono font, three radii, and a logo URL.

Colours are six-digit hex and nothing else — no three-digit form, no `rgb()`, no colour names, no alpha. Radii are whole pixels. An unknown key is refused rather than ignored, so a typo in a token name fails loudly instead of silently doing nothing.

The logo is a URL. There is no upload, and no size is enforced on write — but the email frame caps it at 28 pixels tall and 180 wide, so supply something that survives that.

## The contrast check is a refusal, not a warning

Seven pairs have to reach WCAG AA — 4.5:1 — before a theme saves: both text tones on the canvas and on a surface, the faint tone on the canvas, the action's foreground on the action, and the danger tone on a surface. Below that the write is refused with `contrast_too_low`.

The refusal names every failing pair with its measured ratio **and a nearby colour that would pass**, found by mixing toward black or white until it does. It is the one validation here that does your next step for you rather than leaving you to guess.

Borders are deliberately not checked: a hairline that is hard to see is a choice, and text that is hard to read is not.

## Dark mode, and the moment you lose it

Lath ships a dark palette, and a project still on the default colours gets it: the hosted page emits no colour variables at all, so the visitor's own light or dark setting decides.

**Change any one colour and that stops.** From then on your palette is what every visitor sees, on every device, and the page pins its colour scheme to match your canvas. There is no second palette to fill in — so a brand colour chosen against a white page is the page a dark-mode visitor gets too.

## The words, and where they do not reach

A few hundred copy keys cover the hosted pages end to end — sign-in, sign-up, codes and links, passkeys, two-step, recovery, profile, organizations, preferences — each capped at 500 characters, each interpolating `{name}`-style placeholders where it takes one. `account.theme.get` returns every key and its default, so the list never has to be kept in two places.

Email wears the same theme, but only the published one and only where a template asks for the frame; a template that does not opt in is sent exactly as written. The page-language key applies to the hosted pages alone — the email frame is English, stated in its markup.

Text messages are not themed at all. The single theme value an SMS touches is the unsubscribe word appended to marketing text.

One copy key blocks sending rather than decorating it: the legal postal address is empty by default, and marketing email will not go out until it is set and published.
