Dokumente

Dokumente quer durch alle CRM-Objekte — PDFs, Briefe, Verträge, Schadens-Belege, Identitäts-Nachweise. Full-CRUD top-level über `/v3/documents/{id}` plus parent-scoped Listing/Create unter `/v3/customers/{kid}/documents`.

Zuletzt geprüft: 14. Mai 2026

Übersicht

Ein Dokument ist eine Datei (PDF, JPG, DOCX, …) mit Metadaten — Dateiname, Kategorie, Datum, Ordner, und einer optionalen FK-Bindung an einen Vertrag (vid) oder Schaden (schaden). In der API geht es standardmäßig um die Metadaten. Der Datei-Inhalt kommt opt-in als base64-codierter String über ?with_base64=true mit (kleinere Files) oder binär über /v3/documents/{id}/download beziehungsweise /content.

Das Dokument hat eine global eindeutige id — alle CRUD-Operationen laufen daher top-level über /v3/documents/{id}, ohne Detour über den Customer:

  1. Top-Level Full-CRUD — /v3/documents (List, Create) und /v3/documents/{id} (Read, Update, Delete). Für globale Dokumenten-Searches, Storage-Audits, Compliance-Reports.
  2. Parent-scoped Listing + Create — GET /v3/customers/{kid}/documents und POST /v3/customers/{kid}/documents. Path-kid ist Source-of-Truth; Body darf kein kid-Feld tragen (sonst 400 body_kid_forbidden).

Beide Patterns nutzen denselben Scope v3:documents:* — Dokumente haben eine eigene Scope-Familie, weil sie quer durch alle CRM-Objekte hängen können (Customers, Verträge, Schäden, Aktivitäten, …) und unabhängige Berechtigung brauchen.

Scopes

EndpointScope
GET /v3/documentsv3:documents:read
POST /v3/documentsv3:documents:write
GET /v3/documents/<id>v3:documents:read
GET /v3/documents/<id>/downloadv3:documents:read
GET /v3/documents/<id>/contentv3:documents:read
PATCH /v3/documents/<id>v3:documents:write
DELETE /v3/documents/<id>v3:documents:write
GET /v3/customers/<kid>/documentsv3:documents:read
POST /v3/customers/<kid>/documentsv3:documents:write
Analog /v3/products/<kid>/documents und /v3/users/<kid>/documentsv3:documents:write
GET /v3/contracts/<id>/documents[/<documentId>] und analog für damagesv3:documents:read
POST, PATCH, DELETE auf diesen Nested-Document-Pfadenv3:documents:write

Auth-Mechanik siehe Authentifizierung. Vollständige Scope-Referenz: Scopes.

Pagination & Limits

Standard wie bei Customers. Beachte: Dokumente-Listen können in größeren Orgs sehr groß werden (mehrere zehntausend Einträge) — mit ?limit=5000 und page-Loop arbeiten.

Schema

Die Doku zeigt die echten DB-Spalten der crm_dokumente-Tabelle — Field-Namen sind kein abstrahiertes Public-API-Vertragsmodell. Bei Implementierung gegen diese API: rechne mit den hier gezeigten Schlüsseln.

DB-Tabelle: crm_dokumente. Es gibt keine separate kategorie-Spalte; Documents werden ueber ordner (Folder-ID) sowie die FKs vid/schaden kategorisiert. ext_*/bipro_* Felder sind read-only Metadaten aus externen Quellen (BiPro-Imports, Inbox-Pipelines).

json
{
  "id": 33001,
  "kid": 12345,                       // Customer-ID (Owner)
  "name": "Police_KFZ_2024.pdf",      // Dateiname inkl. Extension
  "extension": "pdf",                 // ohne führenden Punkt
  "size": 245678,                     // Größe in Bytes (integer)
  "datum": "2024-01-15",              // Dokument-Datum (separat von insert_datum)
  "ordner": 5001,                     // Ordner-ID (interne Folder-Struktur)
  "vid": 90001,                       // FK auf Vertrag (null wenn kein Vertrag)
  "schaden": null,                    // FK auf Schaden (null wenn kein Schaden)
  "nr": 4711,                         // optionale interne Dokument-Nummer (integer)
  "url": "12345/2024/police.pdf",     // S3-Path (bei aws=1) oder Datei-Pfad (legacy)
  "dir": "/uploads",                  // S3-Prefix bzw. Verzeichnis
  "aws": 1,                           // 1 = auf S3, 0 = legacy file-storage (smallint)
  "kundenportal": 0,
  "is_eml": 0,
  "protect": 0,
  "has_error": 0,
  "ai_status": 0,                     // KI-Pipeline Status
  "insert_datum": "2024-01-16 08:42:00",
  "edit_datum":   "2024-01-16 08:42:00"
}

Endpoints

Dokumente abfragen

Liefert paginierte Liste aller Dokumente deiner Organisation, optional gefiltert.

Query-Parameter: page (Default 1), limit (Default 50, Max 5000), kid, vid (Vertrags-Filter), schaden (Schaden-Filter), ordner (Folder-Filter), with_schema (Default false, opt-in). Die Liste liefert nur Metadaten — kein base64-Feld, weil die Antwort sonst extrem groß würde. Eine kategorie-Spalte gibt es nicht — fuer Kategorisierung den ordner-Filter bzw. die FKs vid/schaden nutzen.

bash
# Alle Dokumente an einem Vertrag
curl "https://www.api.i-planner.app/v3/documents?vid=90001" \
  -H "Authorization: Bearer $IPLANNER_API_TOKEN"

# Alle Dokumente an einem Schadensfall
curl "https://www.api.i-planner.app/v3/documents?schaden=22" \
  -H "Authorization: Bearer $IPLANNER_API_TOKEN"

Einzelnes Dokument abrufen

GET/v3/documents/<id> Im Playground testen ↗

Liefert ein einzelnes Dokument. Standardmäßig nur Metadaten. Mit ?with_base64=true zusätzlich der Datei-Inhalt als base64-Feld.

Query-Parameter: with_schema (Default false), with_base64 (Default false), file (true/1 liefert den Inhalt binär als Download).

bash
curl "https://www.api.i-planner.app/v3/documents/33001?with_base64=true" \
  -H "Authorization: Bearer $IPLANNER_API_TOKEN" | jq '.'

Für einen binären Abruf stehen zwei explizite Pfade bereit:

  • GET /v3/documents/<id>/download — Content-Disposition: attachment
  • GET /v3/documents/<id>/content — Content-Disposition: inline

Beide liefern dieselben Datei-Bytes wie GET /v3/documents/<id>?file=true.

Dokument top-level anlegen

Customer-kid darf optional im Body stehen. Die Datei-Bytes gehen als base64-codierter String im Body-Feld base64 (oder file als Alias) mit.

Body-Felder:

FeldTypRequiredBeschreibung
base64string✓ (oder file)Datei-Inhalt base64-kodiert
filestring—Alias für base64
namestringempfohlenDateiname inkl. Extension
extensionstring—Optional, wird sonst aus name abgeleitet
datumdate—Dokument-Datum (separat von insert_datum)
kidintoptionalCustomer-Bindung (org-weit ohne kid möglich)
ordnerint—Ordner-ID
vidint—Vertrags-FK
schadenint—Schaden-FK
bash
curl -X POST 'https://www.api.i-planner.app/v3/documents' \
  -H "Authorization: Bearer $IPLANNER_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kid": 12345,
    "name": "Vertragsanlage.pdf",
    "extension": "pdf",
    "ordner": 5001,
    "vid": 90001,
    "datum": "2024-01-15",
    "base64": "JVBERi0xLjQKJeLjz9MK…"
  }'

Dokument am Kunden anlegen

POST/v3/customers/<kid>/documents Im Playground testen ↗

Upload an einem Customer. kid im Pfad ist Source-of-Truth; Body darf kein kid tragen.

Dokumente eines Kunden listen

GET/v3/customers/<kid>/documents Im Playground testen ↗

Dokument an einem CRM-Objekt anhängen

POST/v3/contracts/<id>/documents
POST/v3/damages/<id>/documents

Dokumente lassen sich nur dann direkt an einem CRM-Objekt hochladen, wenn crm_dokumente eine durchsetzbare Fremdschlüssel-Spalte für die Ziel-Section besitzt:

SectionOwnership-SpalteVerhalten
contractsvidList, Read, Download, Create, Patch und Delete sind an die Vertrags-ID gebunden.
damagesschadenAlle Operationen sind an die Schaden-ID gebunden.

Body — identisch zu POST /v3/documents, abzüglich kid (das aus dem Parent abgeleitet wird), vid/schaden (werden beim direkten FK automatisch gesetzt) und base64 Pflicht.

Ordner-Baum eines Parents abfragen

GET/v3/customers/<kid>/documents/folders Im Playground testen ↗
GET/v3/products/<kid>/documents/folders Im Playground testen ↗
GET/v3/users/<kid>/documents/folders Im Playground testen ↗

Liefert die komplette Ordnerstruktur des Parents (Customer, Produktpartner oder Benutzer) als flache Liste. parent_id bildet darin die Hierarchie ab. Das ist praktisch für UI-Picker beim Upload — der ordner-Wert im POST-Body wird über diese Liste aufgelöst.

Für einen gezielten Upload zuerst den Ordnerbaum dieses Parents lesen und dann die zurückgegebene id als ordner im Dokument-POST verwenden. Dieselbe Ownership-Prüfung greift auch beim späteren Verschieben per Dokument-PATCH.

Keine Query-Parameter, keine Pagination — der Baum wird komplett geliefert. Scope: v3:documents:read.

json
{
  "items": [
    { "id": 5001, "name": "Verträge",          "parent_id": null, "kid": 12345 },
    { "id": 5002, "name": "Krankenversicherung","parent_id": 5001, "kid": 12345 },
    { "id": 5003, "name": "KFZ",               "parent_id": 5001, "kid": 12345 },
    { "id": 5010, "name": "Korrespondenz",     "parent_id": null, "kid": 12345 }
  ]
}

parent_id: null markiert Top-Level-Ordner; alle anderen verweisen per parent_id auf den Parent-Ordner. Anti-Recursion ist serverseitig garantiert.

Ordner anlegen

POST/v3/customers/<kid>/documents/folders
POST/v3/products/<kid>/documents/folders
POST/v3/users/<kid>/documents/folders

Legt einen neuen Ordner unter dem Parent (Customer/Produktpartner/Benutzer) an. Scope v3:documents:write.

Body

  • name (string, required) — Anzeigename des Ordners.
  • parent_id (int, optional) — Existierender Ordner unter dem Parent. Default = Standard-Ordner „Dokumente" des Parents (automatisch beim Anlegen des Customers/Produktpartners/Benutzers angelegt). parent_id=0 wird ebenfalls auf den Standard-Ordner gemappt — Frontend rendert nur Ordner die unter dem Standard-Ordner hängen.
  • datum (date, optional) — Anlagedatum (display only).
  • kundenportal (0|1, optional) — Sichtbar im Kundenportal.
  • protect (0|1, optional) — Schützt vor DELETE (Server gibt 403 folder_protected).

Anti-Recursion + Ownership: parent_id muss zum selben Parent (kid) gehören, sonst 400 invalid_parent_id. Self-Parent (parent_id = id) wird beim PATCH abgelehnt. Server pflegt die crm_dokumente_ordner_closure-Tabelle mit (Self-Row + Ancestor-Chain) — neue Ordner sind im Frontend sofort sichtbar.

Ordner umbenennen / verschieben

PATCH/v3/customers/<kid>/documents/folders/<id>
PATCH/v3/products/<kid>/documents/folders/<id>
PATCH/v3/users/<kid>/documents/folders/<id>

PATCH-Semantik. Ändert name, parent_id, kundenportal, protect, datum. Wechsel von parent_id rebuilds Closure-Edges automatisch.

Ordner löschen

DELETE/v3/customers/<kid>/documents/folders/<id>
DELETE/v3/products/<kid>/documents/folders/<id>
DELETE/v3/users/<kid>/documents/folders/<id>

Hard-Delete. Folge-Effekte automatisch:

  • Sub-Folder verlieren ihren parent_id (werden Root unter „Dokumente").
  • Dokumente im Ordner werden auf ordner=0 zurückgesetzt (= unsortiert), nicht gelöscht.
  • Geschützte Ordner (protect=1) werden mit 403 folder_protected abgelehnt.

Dokument aktualisieren

PATCH/v3/documents/<id> Im Playground testen ↗

PATCH-Semantik. Aktualisiert nur die Metadaten — Datei-Inhalt selbst ist nach Upload unveränderlich (Versions-Logik im UI).

Audit-Log Sonderfall — DOCUMENTS_MOVE: PATCH mit ordner=<neue-id> erzeugt KEINEN generischen FIELDS_UPDATE-Eintrag mit IDs, sondern einen DOCUMENTS_MOVE-Eintrag mit:

  • field = Dokument-Name
  • previous_value = alter Ordner-Name
  • current_value = neuer Ordner-Name
  • foreign_section = directories, foreign_section_id = neue Ordner-ID
  • foreign_section_value = <span class="log-foreign">…neuer Ordner…</span>

Andere Felder werden weiter als FIELDS_UPDATE geloggt.

Dokument löschen

DELETE/v3/documents/<id> Im Playground testen ↗

Soft-Delete, idempotent. Volle Error-Tabelle in Fehler.

Annotations

Comments, Followers und Tags hängen top-level am Dokument:

/v3/documents/<id>/comments[/<rid>]
/v3/documents/<id>/followers[/<rid>]
/v3/documents/<id>/tags[/<rid>]