Integrations-API

Konventionen

Formate, Referenzen, Seiten, Änderungsfeed, Dateien, Grenzen und Fehler – was alle Endpunkte gemeinsam haben.

Anfragen und Antworten

  • Inhalte sind JSON (Content-Type: application/json), außer beim Hochladen von Dateien, das ist multipart/form-data.
  • Zeiten sind ISO 8601 mit Zeitzone, z. B. 2026-10-27T09:30:00Z oder 2026-10-27T10:30:00+01:00. DDM antwortet in UTC.
  • IDs sind UUIDs.
  • Fehlermeldungen kommen je nach Accept-Language auf Deutsch oder Englisch. Programme sollten error.code lesen, 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 4711 verwenden.
  • 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:

DokumenteKategorien
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 beidenPOST /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 ReferenzWo
categoryIdscategoryRefsEinspielen, PUT/PATCH eines Dokuments – zusammen 1–20
parentIdparentRefKategorie anlegen und ändern – eines von beiden
categoryIdcategoryRefFilter 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
}
  • currentVersion liefert das ganze Objekt, currentVersion.versionNo nur dieses Feld daraus.
  • Bei titles ist der Marktcode die zweite Ebene: titles.DE,titles.AT. Hat ein Dokument keinen Titel für den Markt, fehlt der Eintrag.
  • Listen wie tags und categoryIds kommen immer ganz.
  • id ist immer dabei. Felder der Liste selbst, z. B. nextCursor, bleiben unberührt.
  • Ein unbekanntes Feld wird mit 400 und 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:

  1. Merken Sie sich das updatedAt des zuletzt verarbeiteten Dokuments.
  2. Fragen Sie beim nächsten Mal mit updatedSince auf diesem Wert, und blättern Sie mit cursor weiter.

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-data mit der Datei im Teil file, bis zur maximalen Dateigröße des Tarifs. Oder JSON mit einer fileUrl, die DDM selbst herunterlädt, mit derselben Grenze.
  • Herunterladen: GET /documents/{id}/content liefert die Datei mit Content-Type, Content-Length und einem ETag.
    • Schicken Sie If-None-Match mit dem letzten ETag, dann antwortet eine unveränderte Datei mit 304 Not Modified und ohne Inhalt.
    • Range: bytes=… setzt einen abgebrochenen Download fort (206 Partial Content).
  • 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

WasGrenze
Anfragen pro Schlüssel600 pro Minute (429, mit Retry-After)
HochladenMaximale Dateigröße des Tarifs und Speicher der Organisation (413)
Download über fileUrlMaximale Dateigröße des Tarifs und Speicher der Organisation (413). Nur HTTP(S), keine Weiterleitungen, höchstens 15 Minuten
Seitengröße1–500
Manifest (/sync)50.000 Dokumente
Referenz512 Zeichen
Schlagwörter pro Objekt50

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.

StatuscodeBedeutungWas tun
400invalid_requestDie Anfrage ist ungültig – siehe issuesAnfrage korrigieren
401unauthorizedKein Schlüssel, oder einer, der ungültig, widerrufen oder abgelaufen istSchlüssel prüfen; nicht wiederholen
403forbiddenDer Schlüssel darf das nicht: fehlende Berechtigung, fehlende Rechte, gesperrte Person, Push-Quelle aus, TarifSchlüssel oder Rechte ändern; nicht wiederholen
404not_foundGibt es nicht, oder dieser Schlüssel sieht es nichtNicht wiederholen
409conflictDer aktuelle Zustand erlaubt es nicht: eine laufende Freigabe, ein Aufbewahrungsschutz, eine Kategorie, die noch Dokumente enthältIm CMS klären, oder anders senden
410goneNicht mehr verfügbar–
413too_largeDie Datei ist zu groß, oder der Speicher ist voll–
415unsupported_media_typeWeder JSON noch MultipartContent-Type korrigieren
422invalid_requestDie Datei konnte nicht von fileUrl geladen oder nicht gespeichert werdenURL prüfen; später wiederholen
429rate_limitedZu viele AnfragenRetry-After Sekunden warten
5xxinternalIn DDM ist etwas schiefgegangenSpäter mit wachsenden Pausen wiederholen
Wiederholen Sie nur 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.