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:
- Top-Level Full-CRUD —
/v3/documents(List, Create) und/v3/documents/{id}(Read, Update, Delete). Für globale Dokumenten-Searches, Storage-Audits, Compliance-Reports. - Parent-scoped Listing + Create —
GET /v3/customers/{kid}/documentsundPOST /v3/customers/{kid}/documents. Path-kidist Source-of-Truth; Body darf keinkid-Feld tragen (sonst400 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
| Endpoint | Scope |
|---|---|
GET /v3/documents | v3:documents:read |
POST /v3/documents | v3:documents:write |
GET /v3/documents/<id> | v3:documents:read |
GET /v3/documents/<id>/download | v3:documents:read |
GET /v3/documents/<id>/content | v3:documents:read |
PATCH /v3/documents/<id> | v3:documents:write |
DELETE /v3/documents/<id> | v3:documents:write |
GET /v3/customers/<kid>/documents | v3:documents:read |
POST /v3/customers/<kid>/documents | v3:documents:write |
Analog /v3/products/<kid>/documents und /v3/users/<kid>/documents | v3:documents:write |
GET /v3/contracts/<id>/documents[/<documentId>] und analog für damages | v3:documents:read |
POST, PATCH, DELETE auf diesen Nested-Document-Pfaden | v3: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).
{
"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.
# 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
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).
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: attachmentGET /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:
| Feld | Typ | Required | Beschreibung |
|---|---|---|---|
base64 | string | ✓ (oder file) | Datei-Inhalt base64-kodiert |
file | string | — | Alias für base64 |
name | string | empfohlen | Dateiname inkl. Extension |
extension | string | — | Optional, wird sonst aus name abgeleitet |
datum | date | — | Dokument-Datum (separat von insert_datum) |
kid | int | optional | Customer-Bindung (org-weit ohne kid möglich) |
ordner | int | — | Ordner-ID |
vid | int | — | Vertrags-FK |
schaden | int | — | Schaden-FK |
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
Upload an einem Customer. kid im Pfad ist Source-of-Truth; Body darf kein kid tragen.
Dokumente eines Kunden listen
Dokument an einem CRM-Objekt anhängen
/v3/contracts/<id>/documents/v3/damages/<id>/documentsDokumente lassen sich nur dann direkt an einem CRM-Objekt hochladen, wenn
crm_dokumente eine durchsetzbare Fremdschlüssel-Spalte für die Ziel-Section
besitzt:
| Section | Ownership-Spalte | Verhalten |
|---|---|---|
contracts | vid | List, Read, Download, Create, Patch und Delete sind an die Vertrags-ID gebunden. |
damages | schaden | Alle 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
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.
{
"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
/v3/customers/<kid>/documents/folders/v3/products/<kid>/documents/folders/v3/users/<kid>/documents/foldersLegt 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=0wird 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 vorDELETE(Server gibt 403folder_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
/v3/customers/<kid>/documents/folders/<id>/v3/products/<kid>/documents/folders/<id>/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
/v3/customers/<kid>/documents/folders/<id>/v3/products/<kid>/documents/folders/<id>/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=0zurückgesetzt (= unsortiert), nicht gelöscht. - Geschützte Ordner (
protect=1) werden mit 403folder_protectedabgelehnt.
Dokument aktualisieren
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-Nameprevious_value= alter Ordner-Namecurrent_value= neuer Ordner-Nameforeign_section=directories,foreign_section_id= neue Ordner-IDforeign_section_value=<span class="log-foreign">…neuer Ordner…</span>
Andere Felder werden weiter als FIELDS_UPDATE geloggt.
Dokument löschen
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>]