# Kunden

> Liste von Handelskunden, Lesevorgang eines Kundenrabatts, vereinbarte Preise und Käufer, und Erstellung oder Aktualisierung von Kunden.

Ein Kunde ist ein Handelskonto: ein Unternehmen, an das Sie verkaufen, mit einem Kontocodex, einem optionalen Kontorabatt und den Personen (Käufers), die sich für es anmelden können.

## Das Kundenobjekt

```json
{
  "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
  "accountCode": "OAK01",
  "name": "Oakfield Joinery",
  "legalName": "Oakfield Joinery Ltd",
  "email": "accounts@oakfieldjoinery.co.uk",
  "discountBp": 500,
  "active": true,
  "newsOptOut": false,
  "catalogueScope": "all",
  "createdAt": "2026-07-02T10:12:44.000Z"
}
```

| Feld             | Typ                    | Beschreibung                                                                                                                                                           |
| ---------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | Zeichenkette           | Kunden-ID.                                                                                                                                                             |
| `accountCode`    | Zeichenkette           | Ihr Kontocodex. Eindeutig im Arbeitsbereich, Großbuchstaben werden ignoriert. Er kann im App geändert werden, also halten Sie den `id` für alles, was langfristig ist. |
| `name`           | Zeichenkette           | Handelsname.                                                                                                                                                           |
| `legalName`      | Zeichenkette oder null | Registrierter Name, wenn unterschiedlich.                                                                                                                              |
| `email`          | Zeichenkette oder null | Hauptkontaktemail, in Kleinbuchstaben.                                                                                                                                 |
| `discountBp`     | Ganzzahl oder null     | Der Rabatt des Kunden auf die gesamte Liste in Basispunkten. `null` für keinen Rabatt.                                                                                 |
| `adjustment`     | Zeichenkette           | `discount` (vom Listenpreis abgezogen) oder `markup` (hinzugefügt). Bestehende Kunden sind `discount`.                                                                 |
| `active`         | Boolesch               | Inaktive Kunden können sich nicht anmelden oder bestellen.                                                                                                             |
| `newsOptOut`     | Boolesch               | Der Kunde hat Angebote und News-E-Mails deaktiviert. Preisänderungse-Mails werden weiterhin gesendet.                                                                  |
| `catalogueScope` | Zeichenkette           | `all` (sieht die gesamte Katalog) oder `approved` (sieht und bestellt nur seine genehmigte Liste). Auf der Kundenpage festgelegt; schreibgeschützt in der API.         |
| `createdAt`      | Zeichenkette           | Wann der Kunde hinzugefügt wurde.                                                                                                                                      |

Wo immer die API einen Kunden benötigt, können Sie den `id` oder den `accountCode` verwenden.

## Kunden auflisten

**GET** `/api/v1/customers`

Alle Kunden, nach Namen sortiert.

| Name     | Typ      | Erforderlich | Beschreibung                        |
| -------- | -------- | ------------ | ----------------------------------- |
| `limit`  | Ganzzahl | nein         | 1 bis 100. Standard 100.            |
| `offset` | Ganzzahl | nein         | Zeilen zu überspringen. Standard 0. |

```bash
curl "https://tradecatalog.app/api/v1/customers?limit=100" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/customers?limit=100', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: customers, nextOffset } = await res.json()
```

```json
{
  "data": [
    {
      "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
      "accountCode": "OAK01",
      "name": "Oakfield Joinery",
      "legalName": "Oakfield Joinery Ltd",
      "email": "accounts@oakfieldjoinery.co.uk",
      "discountBp": 500,
      "active": true,
      "newsOptOut": false,
      "catalogueScope": "all",
      "createdAt": "2026-07-02T10:12:44.000Z"
    }
  ],
  "nextOffset": null
}
```

## Einen Kunden abrufen

**GET** `/api/v1/customers/{id}`

Der Kunde plus alles, was ihre Preise beeinflusst, und wer sich für sie anmelden kann.

| Name | In   | Typ          | Erforderlich | Beschreibung               |
| ---- | ---- | ------------ | ------------ | -------------------------- |
| `id` | Pfad | Zeichenkette | ja           | Kunden-ID oder Kontocodex. |

```bash
curl https://tradecatalog.app/api/v1/customers/OAK01 \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/customers/OAK01', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: customer } = await res.json()
```

```json
{
  "data": {
    "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "accountCode": "OAK01",
    "name": "Oakfield Joinery",
    "legalName": "Oakfield Joinery Ltd",
    "email": "accounts@oakfieldjoinery.co.uk",
    "discountBp": 500,
    "priceListId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "priceList": "Standard",
    "active": true,
    "newsOptOut": false,
    "createdAt": "2026-07-02T10:12:44.000Z",
    "discountRules": [
      {
        "id": "d0e1f2a3-4b5c-4d6e-9f7a-8b9c0d1e2f3a",
        "scope": "product",
        "discountBp": 2000,
        "productId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
        "sku": "HX-8510",
        "target": "Hex bolt M10 x 50 zinc",
        "list": 2280,
        "unit": 1824
      },
      {
        "id": "e1f2a3b4-5c6d-4e7f-8a9b-0c1d2e3f4a5b",
        "scope": "brand",
        "discountBp": 1500,
        "productId": null,
        "sku": null,
        "target": "Northgate",
        "list": null,
        "unit": null
      },
      {
        "id": "f2a3b4c5-6d7e-4f80-9a1b-2c3d4e5f6a7b",
        "scope": "category",
        "discountBp": 1250,
        "productId": null,
        "sku": null,
        "target": "Hinges",
        "list": null,
        "unit": null
      }
    ],
    "agreedPrices": [
      {
        "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "productId": "6c7d8e9f-0a1b-4c2d-9e3f-4a5b6c7d8e9f",
        "price": 1450,
        "note": "Contract price to March 2027",
        "noteVisible": true,
        "internalNote": "Agreed with their buyer",
        "sku": "HX-8512",
        "name": "Hex bolt M12 x 50 zinc",
        "list": 1980,
        "unit": 1450
      }
    ],
    "buyers": [{ "name": "Sam Patel", "email": "sam@oakfieldjoinery.co.uk" }],
    "pendingInvitations": [{ "email": "orders@oakfieldjoinery.co.uk" }]
  },
  "currency": "GBP"
}
```

| Feld                 | Beschreibung                                                                                                                                                                                                                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `priceList`          | Name der Preisliste, von der dieser Kunde kauft. `priceListId` ist die ID dieser Liste. Ein Produkt ohne Preis auf der Liste ist Preis auf Anfrage.                                                                                                                                                           |
| `discountRules`      | Produkt-, Kategorie- und Markenprozentsätze. `scope` ist `product`, `category` oder `brand`; `adjustment` ist `discount` oder `markup`; `target` ist sein Name. Produktregeln geben auch `sku`, `list` und `unit` (was sie zahlen).                                                                           |
| `agreedPrices`       | Vereinbarte (feste) Preise, die aktuell gelten, pro SKU, in Kleinwährungseinheiten, mit dem aktuellen `list`-Preis und dem `unit`-Preis, den sie zahlen. `note` ist der Hinweis; `noteVisible` gibt an, ob dieser Kunde ihn sieht. `internalNote` ist nur für das Personal und wird dem Kunden nie angezeigt. |
| `buyers`             | Personen, die sich für diesen Kunden anmelden und bestellen können.                                                                                                                                                                                                                                           |
| `pendingInvitations` | Ausstehende Einladungen, die noch nicht akzeptiert wurden und nicht abgelaufen sind.                                                                                                                                                                                                                          |

Siehe [wie Preise berechnet werden](/docs/api/prices#how-prices-are-worked-out), um zu sehen, wie diese kombiniert werden.

## Kunden erstellen oder aktualisieren

**PUT** `/api/v1/customers`

Senden Sie bis zu **100 Kunden** pro Aufruf. Jede Zeile wird einem bestehenden Kunden wie folgt zugeordnet:

1. Wenn `accountCode` gesetzt ist, nach Kontocodex. Kein Treffer bedeutet einen neuen Kunden mit diesem Codex.
2. Andernfalls, wenn `email` gesetzt ist, nach E-Mail. Kein Treffer bedeutet einen neuen Kunden.
3. Andernfalls nach Name, Großbuchstaben werden ignoriert.

Neue Kunden ohne Kontocodex erhalten einen aus ihrem Namen erstellten.

### Körper

| Feld                      | Typ                      | Erforderlich | Beschreibung                                                                                                                                                                                                                                     |
| ------------------------- | ------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `customers`               | Array                    | Ja           | 1 bis 100 Kunden. Kontonummern müssen innerhalb des Aufrufs eindeutig sein.                                                                                                                                                                      |
| `customers[].accountCode` | Zeichenfolge oder null   | Ja           | Bis zu 50 Zeichen. `null` zum Abgleich per E-Mail oder Namen.                                                                                                                                                                                    |
| `customers[].name`        | Zeichenfolge             | Ja           | Handelsname, bis zu 160 Zeichen.                                                                                                                                                                                                                 |
| `customers[].email`       | Zeichenfolge oder null   | Ja           | Hauptkontaktemail. `null` löscht sie.                                                                                                                                                                                                            |
| `customers[].discountBp`  | Ganzzahl oder null       | Ja           | Gesamtprozentsatz, 0 bis 10.000, für Rabatt oder Aufschlag. `null` löscht ihn.                                                                                                                                                                   |
| `customers[].adjustment`  | `discount` oder `markup` | Nein         | In welche Richtung `discountBp` den Preis verschiebt. Lassen Sie es aus, um die aktuelle Richtung beizubehalten. Neue Kunden verwenden standardmäßig `discount`.                                                                                 |
| `customers[].priceList`   | Zeichenfolge oder null   | Nein         | Name einer vorhandenen Preisliste. Lassen Sie es aus, um die Liste unverändert zu lassen. `null` fügt den Kunden der Standardliste hinzu. Ein unbekannter Name wird abgelehnt.                                                                   |
| `customers[].region`      | Zeichenfolge oder null   | Nein         | Name einer vorhandenen Region. Lassen Sie es aus, um die Region unverändert zu lassen. `null` löscht sie. Ein unbekannter Name wird abgelehnt.                                                                                                   |
| `customers[].rep`         | Zeichenfolge oder null   | Nein         | E-Mail-Adresse oder Benutzername eines Mitarbeiters, der diesem Kunden zugeordnet ist. Lassen Sie es aus, um den Vertriebsmitarbeiter unverändert zu lassen. `null` löscht ihn. Ein unbekannter E-Mail-Adresse oder Benutzername wird abgelehnt. |

`accountCode`, `name`, `email` und `discountBp` sind erforderlich und werden wie gesendet geschrieben: `null` löscht `email` und `discountBp`. Auf diese Weise kann ein fehlendes Feld in Ihrem Code nicht still einen alten Prozentsatz lassen. `adjustment` und `priceList` sind optional: lassen Sie `adjustment` aus, um einen Aufschlag oder Rabatt so zu lassen, wie er ist.

Die Kundentabelle hat die gleiche Wahl. Neben Rabatt % kann sie eine Anpassungsspalte (`discount` oder `markup`), eine Preislisspalte und eine Vertriebsmitarbeiter-Spalte haben. Die Vertriebsmitarbeiter-Zelle ist die E-Mail-Adresse dieser Person oder ihr Benutzername, wenn sie sich ohne E-Mail anmeldet. Eine leere Zelle lässt das Feld so, wie es ist. Ein unbekannter Listenname oder Vertriebsmitarbeiter ist ein Fehler in dieser Zeile.

Das Aktualisieren eines Kunden sendet keine E-Mail an niemanden oder lädt Käufer ein. Laden Sie Käufer von der Kunden-Webseite in der App ein. Registrierter Name, aktiver Status, Rabattregeln und vereinbarte Preise werden ebenfalls in der App geändert.

```bash
curl -X PUT https://tradecatalog.app/api/v1/customers \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customers": [
      {
        "accountCode": "OAK01",
        "name": "Oakfield Joinery",
        "email": "accounts@oakfieldjoinery.co.uk",
        "discountBp": 500
      },
      {
        "accountCode": "BRK07",
        "name": "Brookside Builders",
        "email": null,
        "discountBp": null
      }
    ]
  }'
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/customers', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ customers }),
})
const { data } = await res.json()
console.log(`${data.created} new, ${data.updated} updated`)
```

```json
{
  "data": { "created": 1, "updated": 1 }
}
```

Die Änderung wird im Aktivitätsprotokoll als Name des Schlüssels angezeigt.
