Konventionen
Anfragen und Antworten
- Inhalte sind JSON (
Content-Type: application/json), außer beim Hochladen von Dateien, das istmultipart/form-data. - Zeiten sind ISO 8601 mit Zeitzone, z. B.
2026-10-27T09:30:00Zoder2026-10-27T10:30:00+01:00. DDM antwortet in UTC. - IDs sind UUIDs.
- Fehlermeldungen kommen je nach
Accept-Languageauf Deutsch oder Englisch. Programme solltenerror.codelesen, der sich nie ändert, nicht die Meldung.
IDs und Referenzen
Jedes Dokument und jede Kategorie hat die eigene id von DDM. Was ein anderes System anlegt, trägt meist auch dessen ID, die Referenz (externalRef):
- Die Referenz eines Dokuments ist innerhalb seiner Push-Quelle eindeutig. Zwei Systeme dürfen beide
4711verwenden. - Die Referenz einer Kategorie gilt im ganzen Kategoriebaum.
Endpunkte unter …/ref/{ref} sprechen ein Objekt über seine Referenz an. Dadurch ist das Einspielen idempotent: Dieselbe Anfrage zweimal ergibt immer noch ein Dokument.
Beide Wege, überall
Jede Operation gibt es in beiden Formen – über die id und über die Referenz:
| Dokumente | Kategorien |
|---|---|
GET, PUT, PATCH, DELETE /documents/{id} | GET, PATCH, DELETE /categories/{id} |
GET, PUT, PATCH, DELETE /documents/ref/{ref} | GET, PUT, PATCH, DELETE /categories/ref/{ref} |
…/content, …/text, …/thumbnail unter beiden | POST /categories legt ohne Referenz an |
Wo eine Anfrage auf andere Objekte verweist, gibt es zu jeder id die passende Referenz – und beide dürfen gemischt werden:
| Über die id | Über die Referenz | Wo |
|---|---|---|
categoryIds | categoryRefs | Einspielen, PUT/PATCH eines Dokuments – zusammen 1–20 |
parentId | parentRef | Kategorie anlegen und ändern – eines von beiden |
categoryId | categoryRef | Filter der Dokumentliste |
{ "categoryIds": ["7a7afae0-f77b-478b-975d-d29eace15d51"], "categoryRefs": ["PIM-CAT-12"] }
Eine Referenz, zu der es nichts gibt, wird mit 400 und dem genauen Feld abgewiesen (z. B. categoryRefs.1). Antworten tragen beides: id und externalRef, bei Dokumenten auch categoryRefs neben categoryIds, bei Kategorien parentRef neben parentId.
Ein Dokument über die Referenz sucht DDM zuerst in der Push-Quelle des Schlüssels, dann in allen Dokumenten, die der Schlüssel sieht – so findet auch ein Nur-Lesen-Schlüssel Dokumente über ihre Referenz. Tragen mehrere Dokumente dieselbe Referenz (aus verschiedenen Quellen), antwortet DDM mit 409; sprechen Sie das Dokument dann über die id an.
Referenzen dürfen beliebige Zeichen enthalten (bis 512), müssen im Pfad aber URL-kodiert sein: PIM/4711 wird zu PIM%2F4711. Dazu dienen encodeURIComponent (JavaScript), urllib.parse.quote(ref, safe="") (Python) oder [uri]::EscapeDataString (PowerShell).
Titel pro Markt
titles ist ein Objekt mit dem Marktcode als Schlüssel, z. B. { "DE": "Preisliste 2026", "AT": "Preisliste 2026" }. GET /markets listet die Codes. Marktcodes sind die Ländercodes der Märkte, z. B. DE, AT oder CH; Groß- und Kleinschreibung spielt keine Rolle. Beim Ändern ändern sich nur die genannten Märkte; ein leerer Text entfernt den Titel dieses Markts. Ein unbekannter Marktcode wird mit 400 abgewiesen.
Nur bestimmte Felder
Mit fields bekommen Sie nur die Felder, die Sie brauchen – bei allen lesenden Anfragen zu Dokumenten und Kategorien, einzeln und in Listen. Die Namen trennen Sie mit Kommas; mit einem Punkt greifen Sie in ein Objekt hinein:
GET /api/ext/v1/documents?fields=name,titles.DE,currentVersion.versionNo,currentVersion.checksum
{
"data": [
{
"id": "8d3f…",
"name": "Preisliste 2026.pdf",
"titles": { "DE": "Preisliste 2026" },
"currentVersion": { "versionNo": 3, "checksum": "9f86d0…" }
}
],
"nextCursor": null
}
currentVersionliefert das ganze Objekt,currentVersion.versionNonur dieses Feld daraus.- Bei
titlesist der Marktcode die zweite Ebene:titles.DE,titles.AT. Hat ein Dokument keinen Titel für den Markt, fehlt der Eintrag. - Listen wie
tagsundcategoryIdskommen immer ganz. idist immer dabei. Felder der Liste selbst, z. B.nextCursor, bleiben unberührt.- Ein unbekanntes Feld wird mit
400und der Liste der möglichen Felder abgewiesen.
Seiten
Listen kommen seitenweise:
{ "data": [ … ], "nextCursor": "WyIyMDI2LTEwLTI3IDA5OjMwOjAwLjEyMzQ1NiswMCIsIjhkM…" }
Für die nächste Seite schicken Sie nextCursor als cursor zurück. Auf der letzten Seite ist nextCursor null. limit setzt die Seitengröße, von 1 bis 500 (Standard 100). Ein Cursor ist undurchsichtig: Bauen Sie keinen selbst und verlassen Sie sich nicht auf seinen Inhalt.
Änderungsfeed
GET /documents ist nach letzter Änderung sortiert, älteste zuerst. Mit updatedSince kommt nur, was zu oder nach diesem Zeitpunkt geändert wurde. Damit ist die Liste ein Änderungsfeed:
- Merken Sie sich das
updatedAtdes zuletzt verarbeiteten Dokuments. - Fragen Sie beim nächsten Mal mit
updatedSinceauf diesem Wert, und blättern Sie mitcursorweiter.
Das letzte Dokument des vorigen Laufs kann noch einmal kommen, weil „zu oder nach“ es einschließt. Verarbeiten Sie Dokumente so, dass zweimal dasselbe nicht schadet.
Als Änderung zählt:
- Neue Bytes (eine neue Version), egal woher.
- Eine Änderung von Name, Status, Weitergabe oder Hervorhebung, über die API oder im CMS.
- Eine Änderung von Schlagwörtern, Titeln oder Ablage über die API.
Schlagwörter, Titel oder Ablage, die im CMS geändert werden, verschieben updatedAt nicht. Wenn Sie diese spiegeln, vergleichen Sie zusätzlich ab und zu das Manifest: Es enthält die categoryIds jedes Dokuments. GET /documents/{id} liefert immer die aktuellen Schlagwörter und Titel.
Gelöschte Dokumente erscheinen nicht im Feed, weil es sie nicht mehr gibt. Um sie zu finden, nehmen Sie das Manifest.
Dateien
- Hochladen:
multipart/form-datamit der Datei im Teilfile, bis zur maximalen Dateigröße des Tarifs. Oder JSON mit einerfileUrl, die DDM selbst herunterlädt, mit derselben Grenze. - Herunterladen:
GET /documents/{id}/contentliefert die Datei mitContent-Type,Content-Lengthund einemETag.- Schicken Sie
If-None-Matchmit dem letztenETag, dann antwortet eine unveränderte Datei mit304 Not Modifiedund ohne Inhalt. Range: bytes=…setzt einen abgebrochenen Download fort (206 Partial Content).
- Schicken Sie
- Jedes Dokument nennt die SHA-256-Prüfsumme seiner aktuellen Version (
currentVersion.checksum). Vergleichen Sie sie mit Ihrer Kopie, um zu wissen, ob Sie überhaupt herunterladen müssen.
Grenzen
| Was | Grenze |
|---|---|
| Anfragen pro Schlüssel | 600 pro Minute (429, mit Retry-After) |
| Hochladen | Maximale Dateigröße des Tarifs und Speicher der Organisation (413) |
Download über fileUrl | Maximale Dateigröße des Tarifs und Speicher der Organisation (413). Nur HTTP(S), keine Weiterleitungen, höchstens 15 Minuten |
| Seitengröße | 1–500 |
Manifest (/sync) | 50.000 Dokumente |
| Referenz | 512 Zeichen |
| Schlagwörter pro Objekt | 50 |
Fehler
Jeder Fehler hat dieselbe Form:
{
"error": {
"code": "invalid_request",
"message": "Bitte Namen eingeben.",
"issues": [{ "path": "name", "message": "Bitte Namen eingeben." }]
}
}
issues ist da, wenn einzelne Felder ungültig sind.
| Status | code | Bedeutung | Was tun |
|---|---|---|---|
| 400 | invalid_request | Die Anfrage ist ungültig – siehe issues | Anfrage korrigieren |
| 401 | unauthorized | Kein Schlüssel, oder einer, der ungültig, widerrufen oder abgelaufen ist | Schlüssel prüfen; nicht wiederholen |
| 403 | forbidden | Der Schlüssel darf das nicht: fehlende Berechtigung, fehlende Rechte, gesperrte Person, Push-Quelle aus, Tarif | Schlüssel oder Rechte ändern; nicht wiederholen |
| 404 | not_found | Gibt es nicht, oder dieser Schlüssel sieht es nicht | Nicht wiederholen |
| 409 | conflict | Der aktuelle Zustand erlaubt es nicht: eine laufende Freigabe, ein Aufbewahrungsschutz, eine Kategorie, die noch Dokumente enthält | Im CMS klären, oder anders senden |
| 410 | gone | Nicht mehr verfügbar | – |
| 413 | too_large | Die Datei ist zu groß, oder der Speicher ist voll | – |
| 415 | unsupported_media_type | Weder JSON noch Multipart | Content-Type korrigieren |
| 422 | invalid_request | Die Datei konnte nicht von fileUrl geladen oder nicht gespeichert werden | URL prüfen; später wiederholen |
| 429 | rate_limited | Zu viele Anfragen | Retry-After Sekunden warten |
| 5xx | internal | In DDM ist etwas schiefgegangen | Später mit wachsenden Pausen wiederholen |
429 und 5xx, mit wachsenden Pausen (z. B. 1, 2, 4, 8 Sekunden). Das Einspielen über eine Referenz ist idempotent, ein wiederholtes PUT ist also immer sicher.