Diese Anleitung zeigt dir, wie du deine Bestände aus X-Sitter in dein eigenes System holst, zum Beispiel um den Bestand in deinem Onlineshop oder ERP aktuell zu halten. Du brauchst nur einen Token und ein Werkzeug, das HTTP-Anfragen schicken kann (cURL, Postman, PHP, Python, JavaScript …).

Für jeden Artikel bekommst du, wie viele Stück in welchem Lager liegen. Auf welchem Lagerplatz die Ware liegt, zeigt die Bestandsabfrage nicht.

## Schritt 1: Voraussetzungen prüfen

- Dein Benutzer hat in X-Sitter den Menüeintrag **Einstellungen > API Zugang** und darf die Bestands- oder Artikelübersicht öffnen.
- **Nur Mandanten:** Dein Logistiker hat dir den Bestand freigegeben. Das ist dieselbe Einstellung, mit der du deinen Bestand in X-Sitter selbst siehst. Ohne sie bietet dir X-Sitter das Recht **Bestand: lesen** für einen Token gar nicht erst an. X-Sitter schaltet diese Einstellung („Lagerbestand zeigen“) auf Wunsch deines Logistikers ein, sprich also bitte mit deinem Logistiker.

## Schritt 2: Token anlegen

1. Klicke in X-Sitter oben rechts auf deinen Namen, dann auf **Einstellungen**.
2. Wähle links **API Zugang**.
3. Trage im Bereich **API-Token** eine **Bezeichnung** ein, z. B. „Bestandsabgleich Shop“.
4. Optional: Setze unter **Gültig bis** ein Datum.
5. Hake unter **Bestand** nur **lesen** an. Die Bestandsabfrage braucht kein anderes Recht.
6. Klicke auf **Token anlegen**.
7. Übernimm den angezeigten Token (er beginnt mit `xst_`) sofort mit **kopieren** und hinterlege ihn sicher in deinem System. Er wird nur dieses eine Mal angezeigt.

!!!warning
Der Token ist so wertvoll wie ein Passwort. Mehr dazu in der [Übersicht](overview.md#token-anlegen).
!!!

## Schritt 3: Erste Anfrage schicken

Ersetze in den Beispielen `xst_YOUR_TOKEN` durch deinen Token. Die erste Anfrage holt die ersten fünf Artikel mit ihrem Bestand:

+++ cURL
```bash
curl "https://app.x-sitter.com/api/v1/stock?per_page=5" \
  -H "Authorization: Bearer xst_YOUR_TOKEN"
```
+++ PHP
```php
<?php
$token = 'xst_YOUR_TOKEN';

$ch = curl_init('https://app.x-sitter.com/api/v1/stock?per_page=5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$result = json_decode($body, true);
print_r($status === 200 ? $result['data'] : $result['error']);
```
+++ Python
```python
import requests

token = "xst_YOUR_TOKEN"

response = requests.get(
    "https://app.x-sitter.com/api/v1/stock",
    params={"per_page": 5},
    headers={"Authorization": f"Bearer {token}"},
    timeout=30,
)
result = response.json()
print(result["data"] if response.ok else result["error"])
```
+++ JavaScript (Node.js)
```js
const token = 'xst_YOUR_TOKEN';

const response = await fetch('https://app.x-sitter.com/api/v1/stock?per_page=5', {
    headers: { Authorization: `Bearer ${token}` },
});
const result = await response.json();
console.log(response.ok ? result.data : result.error);
```
+++

!!!
Schick die Anfragen immer von deinem Server, nie aus JavaScript im Browser deiner Kunden. Sonst kann jeder Besucher deinen Token lesen.
!!!

Bekommst du statt Daten einen Fehler, hilft dir der Abschnitt [Wenn etwas nicht funktioniert](#wenn-etwas-nicht-funktioniert).

## Schritt 4: Antwort verstehen

Eine Antwort sieht so aus:

```json
{
  "data": [
    {
      "article_id": "01999a3c-7e41-7b2a-9d0e-3f6c2a1b4e57",
      "sku": "SKU-100",
      "owner_company_id": 27,
      "quantity": 120,
      "quality_assurance": 8,
      "warehouses": [
        {
          "id": "01999a3c-1111-7b2a-9d0e-3f6c2a1b4e57",
          "name": "Erfurt",
          "quantity": 120,
          "quality_assurance": 8
        }
      ]
    }
  ],
  "meta": { "page": 1, "per_page": 5, "total": 312, "total_pages": 63 }
}
```

| Feld | Bedeutung |
|---|---|
| `article_id` | Eindeutige ID des Artikels in X-Sitter. Damit fragst du später einen einzelnen Artikel ab (Schritt 6). |
| `sku` | Deine Artikelnummer. Damit ordnest du den Bestand deinen Artikeln in deinem Shop oder ERP zu. |
| `owner_company_id` | Firmennummer des Mandanten, dem der Artikel gehört. `null` bei Artikeln des Logistikers selbst. |
| `quantity` | Stück über alle Lager, **einschließlich** der Stück in der Qualitätssicherung. |
| `quality_assurance` | Davon die Stück, die durch die Qualitätssicherung gesperrt sind (z. B. beschädigt oder in Prüfung). |
| `warehouses` | Dieselben Zahlen je Lager mit ID und Name. Leer, wenn der Artikel keinen Bestand hat. |
| `meta` | Seite, Einträge je Seite, Gesamtzahl der Artikel und Seiten. |

!!!success Verkaufbarer Bestand
Für deinen Shop zählt meist `quantity - quality_assurance`, weil gesperrte Ware nicht versendet wird. Im Beispiel oben sind das 120 − 8 = **112** Stück.
!!!

Gut zu wissen:

- Die Liste enthält **alle** deine Artikel, auch die ohne Bestand (dann `quantity: 0` und leere `warehouses`). Mit `in_stock=true` bekommst du nur Artikel mit Bestand (Schritt 5).
- Der Bestand ist das, was im Lager auf den Lagerplätzen liegt. Ware für offene Aufträge zählt so lange mit, bis sie im Lager von ihrem Lagerplatz abgebucht wurde.
- Die Artikel sind nach Artikelnummer sortiert.

## Schritt 5: Alle Seiten abrufen

Die Liste kommt seitenweise, höchstens 500 Artikel je Seite. Für einen vollständigen Abgleich fragst du Seite für Seite ab, bis du `total_pages` erreichst.

+++ PHP
```php
<?php
$token = 'xst_YOUR_TOKEN';
$stock = [];
$page = 1;

do {
    $url = 'https://app.x-sitter.com/api/v1/stock?' . http_build_query(['page' => $page, 'per_page' => 500]);
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
    ]);
    $result = json_decode(curl_exec($ch), true);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($status !== 200) {
        throw new RuntimeException('X-Sitter API: ' . $result['error']['code']);
    }

    foreach ($result['data'] as $article) {
        // Sellable stock = all pieces minus the ones blocked by quality assurance
        $stock[$article['sku']] = $article['quantity'] - $article['quality_assurance'];
    }

    $page++;
} while ($page <= $result['meta']['total_pages']);

print_r($stock);
```
+++ Python
```python
import requests

token = "xst_YOUR_TOKEN"
stock = {}
page = 1

while True:
    response = requests.get(
        "https://app.x-sitter.com/api/v1/stock",
        params={"page": page, "per_page": 500},
        headers={"Authorization": f"Bearer {token}"},
        timeout=30,
    )
    result = response.json()
    if not response.ok:
        raise RuntimeError(f"X-Sitter API: {result['error']['code']}")

    for article in result["data"]:
        # Sellable stock = all pieces minus the ones blocked by quality assurance
        stock[article["sku"]] = article["quantity"] - article["quality_assurance"]

    if page >= result["meta"]["total_pages"]:
        break
    page += 1

print(stock)
```
+++ JavaScript (Node.js)
```js
const token = 'xst_YOUR_TOKEN';
const stock = {};
let page = 1;
let totalPages = 1;

do {
    const url = new URL('https://app.x-sitter.com/api/v1/stock');
    url.search = new URLSearchParams({ page, per_page: 500 });

    const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
    const result = await response.json();
    if (!response.ok) {
        throw new Error(`X-Sitter API: ${result.error.code}`);
    }

    for (const article of result.data) {
        // Sellable stock = all pieces minus the ones blocked by quality assurance
        stock[article.sku] = article.quantity - article.quality_assurance;
    }

    totalPages = result.meta.total_pages;
    page++;
} while (page <= totalPages);

console.log(stock);
```
+++

### Liste eingrenzen

Diese Parameter kannst du beliebig kombinieren:

| Parameter | Beispiel | Wirkung |
|---|---|---|
| `q` | `q=SKU-1` | Nur Artikel, deren Artikelnummer den Text **enthält**. `SKU-1` findet also auch `SKU-10` und `SKU-100`; prüfe bei Bedarf in deinem Programm auf die exakte `sku`. |
| `in_stock` | `in_stock=true` | Nur Artikel mit mindestens einem Stück. |
| `warehouse` | `warehouse=01999a3c-1111-…` | Zählt nur den Bestand dieses Lagers. Die ID steht in jeder Antwort unter `warehouses[].id`. Zusammen mit `in_stock=true` bekommst du nur Artikel, die in diesem Lager liegen. |
| `page` | `page=2` | Welche Seite. |
| `per_page` | `per_page=500` | Artikel je Seite, Standard 25, höchstens 500. |

```bash
curl "https://app.x-sitter.com/api/v1/stock?in_stock=true&per_page=100" \
  -H "Authorization: Bearer xst_YOUR_TOKEN"
```

## Schritt 6: Einzelnen Artikel abfragen

Kennst du die `article_id` (aus einer früheren Antwort), bekommst du den Bestand genau dieses Artikels:

```bash
curl "https://app.x-sitter.com/api/v1/stock/01999a3c-7e41-7b2a-9d0e-3f6c2a1b4e57" \
  -H "Authorization: Bearer xst_YOUR_TOKEN"
```

Die Antwort hat dieselbe Struktur wie ein Eintrag der Liste, nur direkt unter `data` und ohne `meta`. Hast du nur die Artikelnummer, nutze die Liste mit `q=<article number>` und nimm den Eintrag mit der passenden `sku`.

## Schritt 7: Regelmäßig abgleichen

- Ein vollständiger Abgleich alle **15 bis 60 Minuten** reicht für die meisten Shops. Frag nicht jede Sekunde ab; die Zahlen ändern sich nur, wenn im Lager etwas gebucht wird.
- Nutze `per_page=500`, um mit möglichst wenigen Anfragen auszukommen.
- Ob dein System den Token wirklich nutzt, siehst du in X-Sitter unter **Einstellungen > API Zugang** in der Spalte **Zuletzt benutzt**.
- Hast du ein Ablaufdatum gesetzt, lege rechtzeitig einen neuen Token an und tausche ihn in deinem System aus.

## Für Logistiker: Bestand eines Mandanten abfragen

Als Logistiker zeigt dein Token den Bestand aller Artikel, die du verwaltest, auch die deiner Mandanten (erkennbar an `owner_company_id`). Willst du nur die Artikel eines bestimmten Mandanten, schick seine Firmennummer im Header `X-Company`:

```bash
curl "https://app.x-sitter.com/api/v1/stock?in_stock=true" \
  -H "Authorization: Bearer xst_YOUR_TOKEN" \
  -H "X-Company: 27"
```

Die Einstellung „Lagerbestand zeigen“ spielt hier keine Rolle; sie gilt nur für Tokens, die der Mandant selbst anlegt.

## Wenn etwas nicht funktioniert

| Antwort | Ursache | Lösung |
|---|---|---|
| `401 missing_token` | Der Header `Authorization` fehlt oder ist falsch geschrieben. | Schick ihn genau so: `Authorization: Bearer xst_...` (ein Leerzeichen zwischen `Bearer` und dem Token). |
| `401 invalid_token` | Den Token gibt es nicht (mehr), er ist gesperrt oder abgelaufen. | Prüfe seinen Status unter **Einstellungen > API Zugang**, aktiviere ihn bei Bedarf oder lege einen neuen Token an. Schneide beim Kopieren nichts ab. |
| `403 scope_missing` | Der Token hat das Recht **Bestand: lesen** nicht. | Lege einen neuen Token mit diesem Recht an (Rechte lassen sich nachträglich nicht ändern). |
| `403 stock_not_visible` | Du bist Mandant und dein Logistiker hat dir den Bestand nicht (oder nicht mehr) freigegeben. | Bitte deinen Logistiker, ihn freizugeben. |
| `403 company_not_allowed` | Der Header `X-Company` nennt eine Firma, für die der Token nicht arbeiten darf. | Lass `X-Company` weg (Mandanten brauchen ihn nie) oder trage die richtige Firmennummer ein. |
| `404 article_not_found` | Die `article_id` existiert nicht oder der Artikel gehört nicht zu deiner Firma. | Nimm die ID aus einer aktuellen Antwort der Liste. |
| `404 route_not_found` | Die Adresse ist falsch. | Prüfe auf Tippfehler: `https://app.x-sitter.com/api/v1/stock`. |
| `400 invalid_parameter` | `page` oder `per_page` ist keine ganze Zahl. | Schick nur Zahlen, z. B. `per_page=100`. |

Alle Fehler und Rechte auf einen Blick findest du in der [Übersicht](overview.md#fehler). Kommst du nicht weiter, wende dich über **Ticket erstellen** in der Fußzeile von X-Sitter an den Support. Schick Uhrzeit, Adresse und Antwort mit, aber **niemals deinen Token**.
