---
module: claims.vendors
title: Vendors
tab: Internal
order: 20
audience: internal
---

> **Internal · ops/support/admin.** The Vendor Registry (`VendorsPage.svelte`) is a per-institution list of CIT (Cash-in-Transit) and SP (Service Provider) companies used when filing a claim. Backend owner: **hiveops-claims** (`hiveiq-claims`, port 8092). Grounded in code 2026-07-01.

## Who can do what

- **No role gate.** hiveops-claims security is **JWT-authentication-only** (`SecurityConfig` → `.anyRequest().authenticated()`). There is **no** `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` check on vendor endpoints — unlike most HiveOps services. Any authenticated user with a valid JWT can list and create vendors.
- **Isolation is by `institutionKey`, not role.** The controller passes `principal.institutionKey()` (read from the JWT `institutionKey` claim in `JwtAuthenticationFilter`) into every query. A user only ever sees/creates vendors for their own institution. Vendors are private to the institution — there is no cross-institution or MSP "all vendors" view.

## What actions exist (and what's missing)

Only **two** operations are wired end to end:

| Action | UI | API |
|--------|----|-----|
| List vendors (CIT + SP) | Vendor Registry tables, split by type client-side | `GET /api/claims/vendors` |
| Register a vendor | **+ Add Vendor** panel (`AddVendorModal.svelte`) | `POST /api/claims/vendors` |

- **There is NO edit endpoint and NO delete endpoint.** `VendorController` exposes only `@GetMapping` and `@PostMapping`. Once a vendor is created it is permanent — a typo in name/email/phone **cannot be corrected through the app**. Fixing it requires a direct SQL `UPDATE` on the `vendors` table (see below).
- **No de-duplication.** Nothing enforces unique `(institution_key, name)`. Registering "Acme CIT" twice creates two rows; both show in the list and both are assignable to claims.

## First things to check when it misbehaves

1. **"I don't see any vendors" / empty tables** — the list is scoped to the caller's `institutionKey`. If a user logged in with a JWT whose `institutionKey` claim is missing/blank, the query runs against a null/blank key and returns only vendors saved under that same blank key. Confirm the JWT actually carries `institutionKey` (auth service issue, not claims).
2. **"Vendor I added is gone / in the wrong institution"** — same root cause: the vendor was saved with whatever `institutionKey` the JWT had at create time. `institution_key` is nullable and never re-derived. Check the row directly.
3. **"Can't add vendor" toast** — the create panel surfaces the backend message. `name` and `type` are required (`@NotBlank name`, `@NotNull type`); email is only format-validated **client-side** (there is no server email validation). A 400 usually means missing name or an invalid `type` value.
4. **Wrong "Added" date format** — cosmetic only; `createdAt` comes straight from the DB `created_at` (`@CreationTimestamp`).
5. **Vendor exists but not selectable when filing a claim** — that's the claim side, not the registry. The vendor must exist first; assignment happens in the claim (`PUT /api/claims/{uuid}`).

## Direct DB access (for the edit/delete gap)

DB `hiveiq_claims` on the database VM (`10.10.10.188`, via ProxyJump — never the services VM). Table `vendors`.

```bash
# List an institution's vendors
ssh -i ~/.ssh/id_ed25519_hiveiq -J bcosadmin@173.231.195.250 bcosadmin@10.10.10.188 \
  "docker exec hiveiq-postgres psql -U hiveiq -d hiveiq_claims \
   -c \"SELECT uuid, name, type, contact_email, institution_key FROM vendors WHERE institution_key='PBNC' ORDER BY name;\""
```

- **Fix a typo (no app path exists):** `UPDATE vendors SET contact_email='new@vendor.com' WHERE uuid='...';`
- **Remove a bad/duplicate vendor:** deleting is only safe if it is not referenced. `claims.cit_vendor_id` / `claims.sp_vendor_id` are `BIGINT REFERENCES vendors(id)` — a `DELETE` on a referenced vendor will fail the FK constraint. Clear the reference on the claim first, or leave the vendor in place.

## Common failure modes → fix

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Vendor list empty for one user only | JWT missing/blank `institutionKey` | Fix at auth/mgmt; vendors are scoped to that claim |
| Duplicate vendor rows | No unique constraint; double-submit or re-registration | Delete the unreferenced duplicate via SQL |
| Can't edit vendor details | No update endpoint exists (by design/gap) | `UPDATE vendors ...` directly |
| Delete fails with FK error | Vendor assigned to a claim (`cit_vendor_id`/`sp_vendor_id`) | Unassign on the claim, then delete |
| 401 on `/api/claims/vendors` | Missing/expired Bearer token | Re-auth; endpoint requires authentication |

## Related

- User-facing behavior: [claims.vendors] Overview tab.
- Build-it view: [claims.vendors] Architect tab.
- Service-wide facts: [technical.claims] overview.
