# Dokumenten-API

Mit der Dokumenten-API laden Sie Dokumente in einen Kanal hoch, rufen die verarbeiteten Daten ab und melden Workist, ob der Import in Ihr System geklappt hat. Alle Dokumenttypen bieten dieselben Operationen.

Die vollständigen Schemas aller Anfragen und Antworten stehen in der [Swagger-Oberfläche](https://api.workist.com/v1/swagger/). Diese Seite beschreibt, wie Sie die API einsetzen.

## Grundlagen {#grundlagen}

- **Basis-URL:** `https://api.workist.com/v1` (gleichwertig: `https://api.workist.com/api/v1`).
- **Kein Schrägstrich am Ende.** `GET /orders` funktioniert, `GET /orders/` liefert `404`.
- **Authentifizierung:** Jeder Aufruf enthält den Header `Authorization: Bearer <API-Schlüssel>`. Wie Sie einen Schlüssel erstellen, steht im [Quickstart](/docs/api-documentation/quickstart.md#api-schluessel).
- **Kanäle:** Ein API-Schlüssel sieht nur die Kanäle, die für ihn freigegeben sind. Dokumente anderer Kanäle liefert die API nicht aus.
- **Format:** Antworten sind JSON. Uploads senden Sie als `multipart/form-data`, alle anderen Anfragen als JSON.
- **Übertragungsart des Kanals:** Wenn Ihr System die Ergebnisse per API abholt, stellen Sie im Kanal unter **Upload** die Übertragungsart **API** ein. Sonst überträgt Workist die Ergebnisse zusätzlich per SFTP, und erfolgreich übertragene Dokumente erscheinen nicht mehr unter `imported=false`.

## Ressourcen {#ressourcen}

<!-- generated by .claude/tasks/docs-audit/scripts/api_document_reference.py -->

| Dokumenttyp | Ressource |
|---|---|
| Bestellungen | `/orders` |
| Rechnungen | `/invoices` |
| Auftragsbestätigungen | `/order-confirmations` |
| Angebotsanfragen | `/rfq` |
| Lieferscheine | `/delivery-notes` |
| Leistungsverzeichnisse | `/list-of-services` |

## Operationen {#operationen}

Jede Ressource bietet dieselben sieben Operationen. Ersetzen Sie `{ressource}` durch den Pfad aus der Tabelle oben, z. B. `/orders`.

| Methode | Pfad | Zweck |
|---|---|---|
| `GET` | `/{ressource}` | Dokumente auflisten (seitenweise, filterbar) |
| `GET` | `/{ressource}/{id}` | Ein Dokument abrufen |
| `POST` | `/{ressource}` | Datei in einen Kanal hochladen |
| `DELETE` | `/{ressource}/{id}` | Dokument löschen |
| `PATCH` | `/{ressource}/{id}/meta` | Absender und Betreff setzen |
| `PUT` | `/{ressource}/{id}/mark_imported` | Ergebnis des Imports in Ihr System zurückmelden |
| `PUT` | `/{ressource}/{id}/assign_user` | Dokument einem Benutzer zuweisen |
| `POST` | `/list-of-services/{id}/generate-x94` | GAEB-X94-Angebotsdatei aus einem X83-Leistungsverzeichnis erzeugen |

## Typischer Ablauf {#ablauf}

1. Dokumente kommen per E-Mail, über die Workbench oder per `POST /{ressource}` in den Kanal.
2. Ihr System fragt regelmäßig alle abgeschlossenen, noch nicht übergebenen Dokumente ab, z. B. `GET /orders?finished=true&imported=false`, und liest alle Seiten (siehe [Seiten](#seiten)).
3. Für jedes Dokument importiert Ihr System die Daten und meldet das Ergebnis mit `PUT /orders/{id}/mark_imported`: `SUCCEEDED` mit Ihrer Belegnummer oder `FAILED` mit der Fehlermeldung.
4. Gemeldete Dokumente erscheinen in der Abfrage aus Schritt 2 nicht mehr. Mit `FAILED` gemeldete Dokumente kommen zurück, sobald ein Benutzer sie in der Workbench korrigiert und abgeschlossen hat (siehe [Import zurückmelden](#mark-imported)).

Statt regelmäßig abzufragen, können Sie sich per [Webhook](/docs/data-exchange/webhooks.md) benachrichtigen lassen, sobald ein Dokument abgeschlossen ist, und es dann abrufen.

## Dokumente abrufen {#abrufen}

### Filter {#filter}

`GET /{ressource}` akzeptiert folgende Query-Parameter:

| Parameter | Bedeutung |
|---|---|
| `channel` | Kanal-ID. Nur Dokumente dieses Kanals. |
| `assistance_needed` | `true`: nur Dokumente, die auf Bearbeitung in der Workbench warten. `false`: alle anderen. |
| `finished` | `true`: abgeschlossene, nicht verworfene Dokumente. `false`: noch nicht abgeschlossene Dokumente. |
| `imported` | `true`: erfolgreich übergeben (Rückmeldung `SUCCEEDED` oder erfolgreiche Übertragung per SFTP, AS2 oder ERP-Integration). `false`: noch nicht erfolgreich übergeben. Ohne `import_failed` gilt dabei `import_failed=false`. |
| `import_failed` | `true`: Übergabe fehlgeschlagen (z. B. Rückmeldung `FAILED`). `false`: alle anderen. |
| `created_by` | E-Mail-Adresse oder ID des Benutzers, der das Dokument hochgeladen hat. |
| `assisted_by` | E-Mail-Adresse oder ID des Benutzers, der das Dokument bearbeitet hat. |
| `date_from` | Erstellt ab diesem Zeitpunkt, ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`). |
| `date_to` | Erstellt bis zu diesem Zeitpunkt, ISO 8601. |
| `show_unfinished_data` | `true`: `data` auch für Dokumente befüllen, die noch in Verarbeitung sind. Standard: `false`. |
| `page` | Seitennummer, beginnend bei 1. |
| `page_size` | Einträge pro Seite, höchstens 10. |

`GET /{ressource}/{id}` akzeptiert ebenfalls `show_unfinished_data`.

### Seiten {#seiten}

Die Liste liefert **höchstens 10 Dokumente pro Seite**, älteste zuerst. Die Antwort hat diese Form:

```json
{
  "total_count": 37,
  "next": "https://api.workist.com/v1/orders?finished=true&imported=false&page=2",
  "previous": null,
  "items": [ ... ]
}
```

- Rufen Sie `next` auf, bis der Wert `null` ist. Wer nur die erste Seite liest, verpasst Dokumente.
- Wenn Sie Dokumente während des Blätterns zurückmelden, fallen sie aus dem Filter `imported=false` heraus und die folgenden Seiten verschieben sich. Rufen Sie in diesem Fall immer wieder die erste Seite ab, bis `items` leer ist.

### Datensatz {#datensatz}

Jedes Dokument in `items` und jede Einzelabfrage hat diese Felder:

| Feld | Inhalt |
|---|---|
| `id` | ID des Dokuments (UUID). |
| `channel` | ID des Kanals. |
| `status` | Verarbeitungsstatus, siehe [Status](#status). |
| `sender` | Absender, z. B. Absender der E-Mail. |
| `subject` | Betreff der E-Mail oder Dateiname. |
| `created_at` | Zeitpunkt des Eingangs. |
| `updated_at` | Zeitpunkt der letzten Änderung. |
| `created_by` | Benutzer, der das Dokument hochgeladen hat (`first_name`, `last_name`, `email`). |
| `assigned_to` | Zugewiesener Benutzer. |
| `assisted_by` | Benutzer, der das Dokument bearbeitet hat. |
| `assisted_at` | Zeitpunkt der Bearbeitung. |
| `delivered_at` | Zeitpunkt der erfolgreichen Übertragung per SFTP, AS2 oder ERP-Integration. |
| `finished_at` | Zeitpunkt des Abschlusses. |
| `imported_at` | Zeitpunkt, zu dem Ihr System den Import mit `SUCCEEDED` bestätigt hat. |
| `email_file_url` | Link zur Original-E-Mail, falls das Dokument per E-Mail kam. |
| `input_file_url` | Link zur verarbeiteten Datei. |
| `input_file_name` | Ursprünglicher Dateiname. |
| `classified_files` | Weitere Anhänge derselben E-Mail mit `file_name`, `classification` und `url`. |
| `additional_file_urls` | Links zu zusätzlichen Dateien, falls vorhanden. |
| `workbench_url` | Link zum Dokument in der Workbench. |
| `external_reference` | Referenz aus der letzten Rückmeldung, z. B. Ihre Belegnummer. |
| `external_status` | Status aus der letzten Rückmeldung. |
| `external_message` | Nachricht aus der letzten Rückmeldung, z. B. eine Fehlermeldung. |
| `data` | Die extrahierten Dokumentdaten, siehe [Dokumentdaten](#dokumentdaten). |

Die Links zu Dateien sind höchstens 30 Minuten gültig. Laden Sie die Dateien direkt nach dem Abruf herunter oder rufen Sie das Dokument erneut ab, um neue Links zu erhalten.

### Status {#status}

| `status` | Bedeutung |
|---|---|
| `PENDING` | In Verarbeitung. |
| `ON_HOLD` | In der Workbench zurückgestellt. |
| `ASSISTANCE_NEEDED` | Wartet auf Bearbeitung in der Workbench. |
| `ASSISTED` | Bearbeitet, die Verarbeitung läuft weiter. |
| `FINISHED` | Abgeschlossen. |
| `DELIVERED` | Per SFTP, AS2 oder ERP-Integration übertragen. |
| `IMPORTED` | Ihr System hat den Import mit `SUCCEEDED` bestätigt. |
| `FAILED` | Verarbeitung oder Übergabe fehlgeschlagen, auch nach einer Rückmeldung `FAILED`. |
| `REJECTED` | Die Datei konnte nicht gelesen werden. |
| `DISCARDED` | Verworfen. |
| `DELETED` | Gelöscht. Gelöschte Dokumente liefert die API nicht aus. |

### Dokumentdaten {#dokumentdaten}

`data` enthält die Kopffelder des Dokuments und die Positionen unter `line_items` (bei Leistungsverzeichnissen unter `positions`). Die Feldnamen je Dokumenttyp zeigt die [Swagger-Oberfläche](https://api.workist.com/v1/swagger/).

- Es erscheinen nur Werte der Felder, die im Kanal aktiviert sind. Alle anderen Felder sind `null`.
- Solange ein Dokument weder bearbeitet noch abgeschlossen ist, ist `data` leer (`{}`). Mit `show_unfinished_data=true` erhalten Sie den aktuellen Zwischenstand.
- Mengen, Preise und Datumsangaben sind Texte, so wie sie in der Workbench stehen.

## Dokument hochladen {#hochladen}

`POST /{ressource}` mit `multipart/form-data`:

| Feld | Pflicht | Inhalt |
|---|---|---|
| `channel` | Ja | ID des Kanals, in dem das Dokument verarbeitet wird. |
| `file` | Ja | Die Datei. |
| `user` | Nein | E-Mail-Adresse oder ID eines Benutzers, der als Ersteller eingetragen wird. |

- **Kanal-ID:** Sie steht in der Adresszeile der Workbench, wenn der Kanal geöffnet ist (`…/channels/<Kanal-ID>`), und im Feld `channel` jedes Dokuments dieses Kanals. Der Kanal muss zum Dokumenttyp der Ressource passen.
- **Dateitypen:** BMP, CSV, DOC, DOCX, EML, HTML, JPEG, ODS, PDF, PNG, RTF, TIFF, TXT, XLS, XLSM, XLSX und XML. ZIP-Archive, Outlook-Nachrichten (MSG) und `winmail.dat` packt Workist aus und verarbeitet den Inhalt.
- **Antwort:** `201` mit dem neuen Dokument. Wenn eine Datei mehrere Dokumente ergibt (z. B. eine E-Mail mit mehreren Anhängen oder ein ZIP-Archiv), enthält die Antwort nur das erste. Die übrigen finden Sie über `GET /{ressource}`.

## Import zurückmelden {#mark-imported}

Mit `PUT /{ressource}/{id}/mark_imported` melden Sie, ob Ihr System das Dokument übernommen hat. Alle Felder sind optional; ohne Inhalt gilt `SUCCEEDED`.

| Feld | Inhalt |
|---|---|
| `status` | `SUCCEEDED` (Standard): Import erfolgreich, endgültig. `FAILED`: Import abgelehnt, das Dokument geht zurück in die Workbench. `PENDING`: Übergabe noch offen. |
| `external_reference` | Referenz des Dokuments in Ihrem System, z. B. die Auftragsnummer. |
| `external_status` | Status des Dokuments in Ihrem System. |
| `external_message` | Freitext, z. B. die Fehlermeldung Ihres Systems. |

```json
{
  "status": "FAILED",
  "external_reference": "",
  "external_status": "rejected",
  "external_message": "Artikel 4711 ist gesperrt"
}
```

Regeln:

- Nur abgeschlossene Dokumente können zurückgemeldet werden. Sonst antwortet die API mit `400` und `Record not finished yet`.
- `SUCCEEDED` ist endgültig. Eine weitere Rückmeldung zum selben Dokument lehnt die API mit `400` und `Record already successfully delivered` ab.
- `PENDING` lässt das Dokument in der Abfrage `imported=false`, z. B. wenn Ihr System es angenommen, aber noch nicht gebucht hat.
- Jede Rückmeldung überschreibt `external_reference`, `external_status` und `external_message` der vorherigen.

### Was nach `FAILED` passiert {#failed}

1. Das Dokument erhält den Status `FAILED` und gilt nicht mehr als abgeschlossen. Es erscheint wieder in der Liste offener Dokumente in der Workbench.
2. Beim Öffnen sehen Benutzer den Hinweis „Ihr System hat das Dokument mit der folgenden Antwort abgelehnt:" mit Referenz, Status und Nachricht aus Ihrer Rückmeldung.
3. Ein Benutzer klickt auf **Erneut assistieren**, korrigiert die Daten und schließt das Dokument ab, oder er verwirft es mit **Verwerfen**.
4. Nach dem erneuten Abschluss erscheint das Dokument wieder in `GET /{ressource}?finished=true&imported=false`, und Ihr System kann es erneut importieren.

Schreiben Sie deshalb eine verständliche Ursache in `external_message`. Sie ist das, was Ihre Kolleginnen und Kollegen in der Workbench lesen.

## Weitere Operationen {#weitere}

### Absender und Betreff setzen {#meta}

`PATCH /{ressource}/{id}/meta` setzt Absender und Betreff, z. B. wenn Sie Dateien aus einem eigenen Postfach hochladen. Nur mitgesendete Felder werden geändert.

| Feld | Inhalt |
|---|---|
| `sender` | Absender, z. B. Absender der Original-E-Mail. |
| `subject` | Betreff, z. B. Betreff der Original-E-Mail. |

### Benutzer zuweisen {#assign-user}

`PUT /{ressource}/{id}/assign_user` mit `{"user": "<E-Mail-Adresse oder ID>"}` weist das Dokument einem Benutzer Ihrer Organisation zu. Unbekannte Benutzer lehnt die API mit `400` und `{"user": ["User does not exist."]}` ab.

### Dokument löschen {#loeschen}

`DELETE /{ressource}/{id}` löscht das Dokument und antwortet mit `204`. Die API liefert das Dokument danach nicht mehr aus.

### GAEB X94 erzeugen {#gaeb-x94}

Für Leistungsverzeichnisse, die als GAEB-X83-Datei eingegangen sind, erzeugt `POST /list-of-services/{id}/generate-x94` eine X94-Angebotsdatei. Sie senden Ihre Preise je Position und Ihre Lieferantendaten, Workist liefert die XML-Datei zurück.

- `items` (Pflicht): eine Liste mit einem Eintrag je Position. `source_id` ist die GAEB-ID der Position und muss zu einer Position des Leistungsverzeichnisses passen. Optional: `article_number`, `article_characteristics` (`normal`, `alternate` oder `provis`), `quantity`, `unit`, `vat`, `net_price`, `price_basis`, `offer_price`, `price_basis_unit`, `ean`, `article_description`.
- `supplier_info` (Pflicht): `id_no`, `tax_no`, `reg_no` und `address` mit `name1`, `street`, `p_code`, `city`, `country`. Alle Unterfelder sind optional.
- **Antwort:** `200` mit der X94-Datei (`application/xml`) als Download.
- **Fehler:** `400` mit `{"error": "...", "message": "..."}`, z. B. wenn das Dokument keine gültige X83-Datei ist oder eine `source_id` nicht passt.

## Fehler {#fehler}

| HTTP-Status | Ursache | Antwort |
|---|---|---|
| `400` | Ungültige Anfrage | `{"detail": "<Meldung>"}` oder je Feld `{"<feld>": ["<Meldung>"]}`, z. B. `{"channel": ["Channel does not exist"]}` oder `{"file": ["Unsupported file type"]}` |
| `401` | API-Schlüssel fehlt, ist unbekannt oder abgelaufen | `{"detail": "Authentication credentials were not provided."}`, `{"detail": "Invalid token"}` oder `{"detail": "Integration has expired"}` |
| `404` | Dokument nicht gefunden, Kanal nicht für den Schlüssel freigegeben, Seite außerhalb des Bereichs oder Pfad mit `/` am Ende | `{"detail": "<Meldung>"}` |

Ein abgelaufener Schlüssel funktioniert erst wieder, wenn Sie in der Workbench das Ablaufdatum ändern oder entfernen.
