---
title: "Update Issuer Profile"
method: PATCH
path: "/companies/{company_id}/ar/issuer-profiles/{profile_id}"
tags: ["ar-invoices"]
---

# Update Issuer Profile

`PATCH /companies/{company_id}/ar/issuer-profiles/{profile_id}`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/companies/{company_id}/ar/issuer-profiles/{profile_id}`
- Auth: none declared

Partial update of one issuer profile — an omitted field is left unchanged.

ZCT-265: this route is declared PATCH but bound the *create* schema and dumped
it whole, so an omitted field was written at its Pydantic default. A caller
doing exactly what the ``ar_invoice_readiness`` gate asks — completing the
address after ``issuer_incomplete`` — silently cleared ``bank_account_id`` (the
creditor account printed on the Swiss QR-bill), ``default_payment_terms_id``
and ``is_default``, and reset ``country``/``template_key``. Nothing in the
response distinguished the fields it had changed from the ones it had wiped.

An explicit ``null`` still clears a nullable field; the five NOT NULL columns
are refused by name (``ar_invoice._NON_NULLABLE_ISSUER_FIELDS``).

## Path parameters

- `company_id` string, required
- `profile_id` string, required

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Request body

- ArIssuerProfileUpdate: PATCH body — only the fields actually sent are applied (``exclude_unset``). ZCT-265: the PATCH route used to bind :class:`ArIssuerProfileIn` and ``model_dump()`` it whole, so every field the caller omitted was written at its Pydantic **default** — not merely nulled. A partial update therefore cleared ``bank_account_id`` (the creditor account printed on the Swiss QR-bill), ``default_payment_terms_id`` and ``is_default``, and reset ``country`` to ``"CH"`` and ``template_key`` to ``"standard"``, silently. The house rule is the one ``counterparty`` states outright — "unset fields are left untouched (no reset-to-default)" — and the twelve sibling PATCH routes that dump with ``exclude_unset`` (``GlAccountUpdate``, ``PayrollEmployeeUpdate``, the payroll rate/expense patches, accounting settings) all pair an all-optional schema with that dump. This is that schema. **An explicit ``null`` still clears** — that is what separates "omitted" from "cleared", and the SPA relies on it (choosing "none" for the creditor bank account sends ``bank_account_id: null``). The five columns that are NOT NULL in the database — ``label``, ``legal_name``, ``country``, ``template_key``, ``is_default`` — are refused by name in ``ar_invoice.update_issuer_profile`` rather than left to trip an opaque ``constraint violation`` 422 (the ZET-139 lesson, whose ``_NON_NULLABLE_ACCOUNT_FIELDS`` guard this mirrors). Deliberately a standalone declaration rather than a shared ``_Fields`` base (the ``PayrollEmployeeUpdate`` variant): extracting one would reorder ``ArIssuerProfileIn``'s properties in the emitted OpenAPI and churn both frontends' generated clients for no behavioural gain. ⚖️ **ZCT-513 applies to updates too, as a no-clear rule rather than a required field.** The ruling is "enforce at the API/schema layer for creates *and* updates", and the three address parts stay ``| None`` here on purpose: making them required would undo ZCT-265 — every PATCH that means to change only ``footer_text`` would have to resend an address, which is the wholesale-replace shape this schema exists to end. What is refused is a *write that empties them*: an explicit ``null`` or a blank string on ``address_line1``/``postal_code``/ ``city``. Omission stays inert, so a legacy address-less profile can still be edited field by field, and no new address-less row can be produced from a complete one. (The NOT NULL columns are refused a rung lower, by name, in ``ar_invoice._NON_NULLABLE_ISSUER_FIELDS``; these three are genuinely nullable in the database and stay so — the ruling forbids a NOT NULL constraint.)
  - `label` string, nullable
  - `legal_name` string, nullable
  - `address_line1` string, nullable
  - `address_line2` string, nullable
  - `postal_code` string, nullable
  - `city` string, nullable
  - `country` string, nullable
  - `vat_number` string, nullable
  - `contact_email` string, nullable
  - `contact_phone` string, nullable
  - `bank_account_id` string, nullable
  - `logo_document_id` string, nullable
  - `footer_text` string, nullable
  - `legal_text` string, nullable
  - `default_language` 'de' | 'en' | 'fr' | 'it', nullable
  - `default_payment_terms_id` string, nullable
  - `template_key` string, nullable
  - `is_default` boolean, nullable

## Response `200`

Successful Response

- ArIssuerProfileOut
  - `label` string, required
  - `legal_name` string, required
  - `address_line1` string, nullable
  - `address_line2` string, nullable
  - `postal_code` string, nullable
  - `city` string, nullable
  - `country` string
  - `vat_number` string, nullable
  - `contact_email` string, nullable
  - `contact_phone` string, nullable
  - `bank_account_id` string, nullable
  - `logo_document_id` string, nullable
  - `footer_text` string, nullable
  - `legal_text` string, nullable
  - `default_language` string, nullable
  - `default_payment_terms_id` string, nullable
  - `template_key` string
  - `is_default` boolean
  - `id` string, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
