---
module: claims.settings
title: Settings
tab: Claude
order: 40
audience: dev
---

Terse reference. Feature = **vendor email templates** shown on the Claims Settings screen (read-only display of the *effective* template).

## Ownership
- **Owner service:** `hiveops-claims` (image `hiveiq-claims`, port 8092, DB `hiveiq_claims`). No other service. No Kafka in this feature.
- External route: `api.bcos.cloud/claims/...` → `hiveiq-claims:8092/api/claims/...`
- Frontend view: `hiveops-claims/frontend/src/components/Settings/EmailTemplatesSection.svelte` (calls `effective` only).

## Endpoints (METHOD path)
| METHOD | Path | Auth | Result |
|--------|------|------|--------|
| GET | `/api/claims/settings/email-templates/effective` | JWT | 200 `{name,subject,global}` / **204** if none |
| GET | `/api/claims/settings/email-templates` | JWT | list (own institution) |
| POST | `/api/claims/settings/email-templates` | JWT | 201; `{name,subject,bodyHtml,isDefault}` |
| PUT | `/api/claims/settings/email-templates/{uuid}` | JWT | 403 if not owner, 404 if uuid unknown |
| DELETE | `/api/claims/settings/email-templates/{uuid}` | JWT | 204 |
| GET/POST/PUT/DELETE | `/api/internal/email-templates?institutionKey=<key>` | header `X-Internal-Secret` | cross-institution / `__GLOBAL__` |

## Table
- `email_templates` (DB `hiveiq_claims`, migration `V2__email_templates.sql`)
- Cols: `id`, `uuid`, `institution_key`, `name`, `subject`, `body_html`, `is_default`, `created_at`, `updated_at`
- Global inherited default row: `institution_key = '__GLOBAL__'`, `is_default = true`

## Topics
- None for this feature. (Service consumes `hiveops.device.events` for `device_summary` only — unrelated.)

## Roles / auth
- **No `hasAnyRole` anywhere.** `SecurityConfig` → `anyRequest().authenticated()`. Any valid JWT works.
- Scope = `institutionKey` from JWT (`ClaimsPrincipal`). Write ops enforce `institutionKey == template.institution_key` else 403.
- `/api/internal/**` = `permitAll` in security, but each method requires `X-Internal-Secret` == `INTERNAL_SERVICE_SECRET` (blank default ⇒ all rejected).

## Resolution logic (effective + send path, identical)
1. own institution default (`is_default=true`) →
2. `__GLOBAL__` default →
3. none → 204 (UI) / hardcoded fallback body (email send)

## Template placeholders (subject + body_html, HTML-escaped)
`{{claimNumber}}` `{{terminalId}}` `{{location}}` `{{shortageAmount}}` `{{claimStatus}}` `{{vendorName}}` `{{messageText}}` `{{senderEmail}}`

## Gotchas
- `{uuid}` path var is the `uuid` column, NOT numeric `id`.
- Only ONE default per institution — setting `isDefault=true` auto-clears others (`clearDefaultForInstitution`).
- Settings SPA is **read-only**: `list/create/update/delete` exist in `api.ts` + backend but are NOT wired to any UI. Manage templates via API only.
- Template used by `EmailService.sendMessageNotification` (message emails). **Attachment** emails use a hardcoded body, not the template.
- Email send silently skips if `MAIL_USERNAME`/`MAIL_PASSWORD` unset (no `JavaMailSender` bean); look for DEBUG "Mail not configured".
- Dev profile = H2 create-drop, Flyway off → templates vanish on restart.
- `MAIL_FROM` default `noreply@bcos.cloud`; `EmailService` uses `noreply@hiveops.com` only when blank.

## Do NOT
- Do NOT add/expect a role check (`MSP_ADMIN`/`BCOS_ADMIN`) — none exists here; don't document one.
- Do NOT edit another institution's template via the JWT endpoints (403); use `/api/internal/**` with the secret for `__GLOBAL__`/cross-institution.
- Do NOT call `/api/internal/**` without a non-blank `INTERNAL_SERVICE_SECRET` set on the container — every call 401s.
- Do NOT assume a Kafka topic or executor→record-store flow for this feature — it's synchronous DB CRUD.
- Do NOT reference "HiveOps" in user-facing copy — brand is **HiveIQ**; `hiveops-ai` = **Adoons**.
