# Benutzung der Stammdaten-API

Workist benötigt die Stammdaten aus Ihrem ERP-System. Per API übermitteln Sie sie:

- als CSV-Datei,
- als Link zu einer öffentlich erreichbaren CSV-Datei,
- als Liste von Objekten im JSON-Format, bei großen Mengen in mehreren Blöcken.

Jeder Import ersetzt den gesamten Bestand der Lookup-Definition: Zeilen, die im neuen Import fehlen, löscht Workist. Senden Sie deshalb immer den vollständigen Bestand. Delta-Importe richtet Workist auf Anfrage ein.

API Dokumentation (Swagger): [https://api.workist.com/v1/swagger/](https://api.workist.com/v1/swagger/)

## Grundlagen {#grundlagen}

- **Basis-URL:** `https://api.workist.com/v1/master-data`
- **Authentifizierung:** derselbe API-Schlüssel wie für die Dokumenten-API, im Header `Authorization: Bearer <API-Schlüssel>` (siehe [Quickstart](/docs/api-documentation/quickstart.md#api-schluessel)).
- **Lookup-Definition:** Jeder Import gehört zu genau einer Lookup-Definition, angegeben mit `lookup_definition_id`. Wo Sie die ID finden, steht unter [Lookup Definition IDs](#lookup-definition-ids).
- **Kein Schrägstrich am Ende** der Pfade.

## Endpunkte {#endpunkte}

Für jeden Stammdatentyp gibt es einen eigenen Endpunkt, der einen Import anlegt. Die Lookup-Definition muss zum Typ des Endpunkts passen. Jeder Pfad führt zur Beschreibung des Endpunkts in Swagger.

| Stammdaten | Endpunkt | Lookup-Typ | Spalten |
|---|---|---|---|
| Artikel und kundenspezifische Artikel | [`POST /master-data/articles/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/articles_imports_create) | `ARTICLE_LOOKUP` | [Beschreibung](/docs/master-data/articles.md) |
| Kunden | [`POST /master-data/clients/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/clients_imports_create) | `CLIENT_LOOKUP` | [Beschreibung](/docs/master-data/customers.md) |
| Kundenkontakte | [`POST /master-data/contacts/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/contacts_imports_create) | `CONTACT_LOOKUP` | [Beschreibung](/docs/master-data/customer-contacts.md) |
| Liefer- und Rechnungsempfänger | [`POST /master-data/addresses/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/addresses_imports_create) | `ADDRESS_LOOKUP` | [Beschreibung](/docs/master-data/delivery-and-invoice-recipients.md) |
| Einheitenkonvertierung | [`POST /master-data/conversion-factors/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/conversion_factors_imports_create) | `CONVERSION_FACTOR_LOOKUP` | [Beschreibung](/docs/master-data/unit-conversion.md) |
| Rahmenverträge | [`POST /master-data/framework-contracts/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/framework_contracts_imports_create) | `FRAMEWORK_CONTRACT_LOOKUP` | [Beschreibung](/docs/master-data/framework-agreements.md) |
| Abzugleichende Bestellungen | [`POST /master-data/order-matching/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/order_matching_imports_create) | `ORDER_MATCHING_LOOKUP` | [Beschreibung](/docs/master-data/orders-to-be-matched.md) |
| Angebote | [`POST /master-data/offer-matching/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/offer_matching_imports_create) | `OFFER_MATCHING_LOOKUP` | [Beschreibung](/docs/master-data/quotes.md) |

Dazu kommen allgemeine Endpunkte für alle Typen:

| Methode | Pfad | Zweck |
|---|---|---|
| `GET` | [`/master-data/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/imports_list) | Importe auflisten (seitenweise, bis 1000 pro Seite) |
| `POST` | [`/master-data/imports`](https://api.workist.com/v1/swagger/#/Master%20Data/imports_create) | Import anlegen, für jeden Stammdatentyp |
| `GET` | [`/master-data/imports/{import_id}`](https://api.workist.com/v1/swagger/#/Master%20Data/imports_retrieve) | Status eines Imports abrufen |
| `POST` | [`/master-data/imports/{import_id}/data`](https://api.workist.com/v1/swagger/#/Master%20Data/imports_data_create) | Weiteren Datenblock an einen Batch-Import anhängen |
| `POST` | [`/master-data/imports/{import_id}/start`](https://api.workist.com/v1/swagger/#/Master%20Data/imports_start_create) | Batch-Import starten |

### Anfragefelder {#anfragefelder}

Ein Import enthält genau eines der Felder `file`, `file_url` oder `data`. Senden Sie die Anfrage als `multipart/form-data` oder als JSON. Bei `multipart/form-data` übergeben Sie `data` als JSON-Text.

| Feld | Inhalt |
|---|---|
| `lookup_definition_id` | ID der Lookup-Definition (Pflicht). |
| `batched` | `true`: Batch-Import anlegen, der weitere Datenblöcke annimmt. Standard: `false` (Import startet sofort). |
| `file` | CSV-Datei (Dateiendung `.csv`), höchstens 10 MB. |
| `file_url` | Öffentlich erreichbarer `http`/`https`-Link auf eine CSV-Datei. Der Pfad endet auf `.csv`, die Datei hat höchstens 10 MB. |
| `data` | Liste von Objekten (JSON), höchstens 10.000 Zeilen. Alle Objekte brauchen dieselben Felder. |

Die Feldnamen der Objekte und CSV-Spalten sind die Spaltennamen der jeweiligen Stammdatenseite (Spalte „Spalten" oben). JSON-Daten wandelt Workist in eine kommagetrennte CSV-Datei um. Haben Sie für die Lookup-Definition eine [Transformer-Konfiguration](/docs/master-data/transformer-configuration.md) hinterlegt, z. B. Spaltenzuordnungen oder ein anderes Trennzeichen, gilt sie auch für Importe per API.

## Antwort und Import-Status {#import-status}

Workist verarbeitet Importe im Hintergrund. Jeder Aufruf, der einen Import anlegt, Daten anhängt oder startet, antwortet mit `202` und der Import-ID:

```json
{"import_id": "0b7c6a52-5d0e-4f43-9a8e-3c2b1d0e9f11"}
```

Der Header `Location` enthält die Adresse für den Status. Fragen Sie ihn mit `GET /master-data/imports/{import_id}` ab, bis `status` den Wert `SUCCESS`, `FAILED` oder `CANCELED` hat:

```python
import time

url = f"{base_url}/imports/{import_id}"
while True:
    run = requests.request("GET", url, headers=headers).json()
    if run["status"] in ("SUCCESS", "FAILED", "CANCELED"):
        break
    time.sleep(60)
# z. B. {"import_id": "...", "status": "SUCCESS", "lookup_type": "ARTICLE_LOOKUP", ...}
```

Die Antwort enthält:

| Feld | Inhalt |
|---|---|
| `import_id` | ID des Imports. |
| `customer_id` | ID Ihrer Organisation. |
| `lookup_type` | Stammdatentyp, z. B. `ARTICLE_LOOKUP`. |
| `lookup_definition` | ID der Lookup-Definition. |
| `ingestion_file_url` | Speicherort der zusammengeführten Importdatei. |
| `ingestion_file_num_rows` | Anzahl der importierten Zeilen. |
| `error_type` | Fehlerart, falls der Import fehlgeschlagen ist. |
| `error_message` | Fehlermeldung, falls der Import fehlgeschlagen ist. |
| `trigger` | Auslöser, z. B. `PUBLIC_API`, `SFTP_FILE_UPLOAD` oder `WORKBENCH_FILE_UPLOAD`. |
| `created_at` | Angelegt am. |
| `updated_at` | Zuletzt geändert am. |
| `started_at` | Verarbeitung gestartet am. |
| `finished_at` | Verarbeitung beendet am. |
| `status` | Status, siehe unten. |
| `input_file_url` | Speicherort der ersten übermittelten Datei bzw. des ersten Datenblocks. |
| `input_file_num_rows` | Zeilen in dieser Datei. |

| `status` | Bedeutung |
|---|---|
| `PENDING` | Angelegt, noch nicht gestartet (Batch-Import wartet auf Daten oder Start). |
| `RUNNING` | Wird verarbeitet. |
| `SUCCESS` | Erfolgreich abgeschlossen. |
| `FAILED` | Fehlgeschlagen, siehe `error_type` und `error_message`. |
| `CANCELED` | Abgebrochen. |

Alle Importe, auch die per API, sehen Sie außerdem im [Stammdaten-Hub](/docs/master-data/master-data-hub.md) der Workbench.

## Grenzen {#grenzen}

- **Pro Anfrage:** `data` mit höchstens 10.000 Zeilen, `file` und `file_url` mit höchstens 10 MB. Größere Bestände übertragen Sie als [Batch-Import](#massen-importe).
- **Pro Stunde:** Wurden für eine Lookup-Definition in der letzten Stunde mehr als 12 Importe angelegt, lehnt die API weitere mit `400` ab (`Too many imports scheduled for this lookup definition recently`). Importe aus allen Quellen zählen mit, auch Uploads per SFTP. Ein SFTP-Upload über der Grenze wird ohne Meldung übersprungen.
- **`file_url`:** nur `http` und `https`. Adressen, die auf interne oder private Netze zeigen, lehnt die API ab. Workist lädt die Datei während der Anfrage herunter.

## Batch-Importe {#massen-importe}

Für Bestände über 10.000 Zeilen oder 10 MB legen Sie einen Batch-Import an und übertragen die Daten in Blöcken:

1. Batch-Import mit `"batched": true` anlegen, z. B. über `POST /master-data/articles/imports`. Die Antwort enthält die `import_id`.
2. Datenblöcke anhängen mit `POST /master-data/imports/{import_id}/data`, so oft wie nötig. CSV-Blöcke brauchen alle dieselbe Kopfzeile.
3. Import starten mit `POST /master-data/imports/{import_id}/start`. Danach nimmt der Import keine Daten mehr an.

```python
import requests
import json

def chunk(it, size):
    from itertools import islice
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

base_url = "https://api.workist.com/v1/master-data"
lookup_definition_id = "<Article Lookup Definition ID>"
token = "<Your Auth Token>"
headers = {"Authorization": f"Bearer {token}"}

articles = [
    {"article_id1": "123", "description": "abc", "order_units": ["PCS", "KG"]},
    {"article_id1": "456", "description": "def", "order_units": ["KG"]},
    {"article_id1": "789", "description": "def", "order_units": ["M"]}
]
articles_chunked = list(chunk(articles, 10000))

url = f"{base_url}/articles/imports"
response = requests.request("POST", url, headers=headers, data={
    "lookup_definition_id": lookup_definition_id,
    "batched": True,
})
import_id = response.json().get("import_id")

for articles_batch in articles_chunked:
    url = f"{base_url}/imports/{import_id}/data"
    response = requests.request("POST", url, headers=headers, data={"data": json.dumps(articles_batch)})

url = f"{base_url}/imports/{import_id}/start"
response = requests.request("POST", url, headers=headers)
```

## Kunden {#kunden}

```python
clients = [
    {"client_id1": "C-001", "company_name": "Test SE", "address1": "Test street 1", ...},
    {"client_id1": "C-002", "company_name": "Muster GmbH&Co. KG", ...}
]
url = f"{base_url}/clients/imports"
payload = {"lookup_definition_id": "<your lookup definition>", "data": json.dumps(clients)}
response = requests.request("POST", url, headers=headers, data=payload)
```

## Lieferadressen {#lieferadressen}

Verknüpfung mit Kunden über `partition_id`. Das Feld für die Lieferadresse sollte ausschließlich Firmennamen enthalten.

```python
delivery_addresses = [
    {"partition_id": "C-001", "address_id1": "L-001", "name": "Tester 2 GmbH", "address1": "Test street 2", ...}
]
url = f"{base_url}/addresses/imports"
payload = {"lookup_definition_id": "<your lookup definition>", "data": json.dumps(delivery_addresses)}
response = requests.request("POST", url, headers=headers, data=payload)
```

## Artikel {#artikel}

`order_units` muss als Array übermittelt werden (z. B. `["KG", "STK"]`).

```python
articles = [
    {"article_id1": "ART-001", "description": "Sensor Module", "order_units": ["PCE", "KG"]},
]
url = f"{base_url}/articles/imports"
payload = {"lookup_definition_id": "<your lookup definition>", "data": json.dumps(articles)}
response = requests.request("POST", url, headers=headers, data=payload)
```

## Kundenspezifische Artikel {#kundenspezifische-artikel}

Jeder Eintrag braucht `partition_id` (Kundenverweis) und `article_id1` (interner Artikel). `article_id2` enthält die Kundenartikelnummer.

```python
client_articles = [
    {"partition_id": "C-001", "article_id1": "ART-001", "article_id2": "CUST-ART-555"},
]
url = f"{base_url}/articles/imports"
payload = {"lookup_definition_id": "<your client-specific lookup definition>", "data": json.dumps(client_articles)}
response = requests.request("POST", url, headers=headers, data=payload)
```

## Fehler {#fehler}

| HTTP-Status | Ursache | Antwort |
|---|---|---|
| `400` | Ungültige Anfrage, z. B. fehlende `lookup_definition_id`, Datei keine CSV-Datei, zu viele Zeilen, Lookup-Definition passt nicht zum Endpunkt, Import nicht mehr im Status `PENDING`, zu viele Importe pro Stunde | `{"detail": "<Meldung>"}` oder `["<Meldung>"]` |
| `401` | API-Schlüssel fehlt | `{"detail": "Authentication credentials were not provided or invalid."}` |
| `503` | Die Anfrage hat länger als 30 Sekunden gedauert | `{"detail": "Please try again later"}` |

## Postman Collection {#postman-collection}

Eine Postman Collection für die Stammdaten-API ist auf Anfrage verfügbar. Wenden Sie sich an Ihren Workist-Ansprechpartner oder an [support@workist.com](mailto:support@workist.com).

## Lookup Definition IDs {#lookup-definition-ids}

Lookup-Definitionen legen Benutzer mit der Rolle Manager, IT oder Admin im [Stammdaten-Hub](/docs/master-data/master-data-hub.md) an. Die ID finden Sie an zwei Stellen:

- im Stammdaten-Hub: in der Übersicht in der Spalte ID und auf der Detailseite der Lookup-Definition,
- im Kanal unter **Kanal verwalten** → **Stammdaten**, unter der jeweils ausgewählten Lookup-Definition.

Weitere Details: [Lookup-IDs abrufen](/docs/data-exchange/api.md#lookup-ids)
