> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langdock.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Benutzerdefinierte Integrationen Erstellen

> Nutzerdefinierte Integrationen ermöglichen es dir, jedes API-fähige Tool mit einem Agenten zu verbinden. Dies eröffnet dir unendliche Möglichkeiten. Unten findest du eine umfassende Anleitung zum Erstellen von Integrationen, Aktionen und Triggern.

## Integrationen vs. Aktionen vs. Trigger

**Integrationen** sind standardisierte Verbindungen zwischen Langdock und Drittanbieter-Tools, die Authentifizierung und API-Kommunikation verwalten. Innerhalb jeder Integration kannst du Folgendes erstellen:

* **Aktionen**: Funktionen, die Agenten und Workflows aufrufen können, um mit APIs zu interagieren (z.B. "Ticket erstellen", "E-Mail senden", "Daten abrufen")
* **Trigger**: Event-Monitore, die Workflows starten, wenn bestimmte Ereignisse auftreten (z.B. "neue E-Mail erhalten", "Datei hochgeladen")

## Eine Integration einrichten

Klicke im [Integrationsmenü](https://app.langdock.com/integrations) auf `Integration hinzufügen`, um zu beginnen.

Gib als Nächstes einen Integrationsnamen an und lade ein Icon hoch (wird im Chat bei der Verwendung von Aktionen und in der Integrationsübersicht angezeigt). Füge eine Beschreibung hinzu, damit Agenten wissen, wann sie diese Integration verwenden sollen. Klicke auf `Speichern`, um sie zu erstellen.

### Authentifizierung

Beginne mit der Authentifizierung im Tab `Erstellen`. Wähle deinen Authentifizierungstyp und konfiguriere ihn gemäß den folgenden Schritten:

### API-Schlüssel

<Info>
  **Was ist ein API-Schlüssel?** Ein API-Schlüssel ist ein eindeutiger Identifikationscode, der zur Authentifizierung von Anfragen an eine API verwendet wird. Ähnlich wie ein Passwort autorisiert er den Zugriff auf externe Dienste.
</Info>

Nach der Auswahl der API-Schlüssel-Authentifizierung füge in Schritt 2 benutzerdefinierte Eingabefelder hinzu (wie API-Schlüssel oder Client-ID). Diese Eingaben werden erfasst, wenn Nutzer Verbindungen einrichten und können als "erforderlich" markiert werden.

In Schritt 3 kannst du einen Test-API-Endpunkt einrichten, um die Authentifizierung zu validieren. Ersetze den URL-Parameter und füge Verweise auf deine Eingabefelder mit `data.auth.fieldId` hinzu.

Verwende die integrierten Funktionen `ld.request` und `ld.log` für Anfragen und Protokollierung.

Teste deine Aktion und erstelle deine erste Verbindung.

### OAuth 2.0

<Info>
  **Was ist OAuth 2.0?** OAuth 2.0 ist ein Autorisierungsprotokoll, das es Anwendungen ermöglicht, sicheren Zugriff auf Nutzerdaten von anderen Services zu erhalten, ohne dass Passwörter weitergegeben werden müssen.
</Info>

Nutzerdefinierte Integrationen unterstützen OAuth 2.0-Authentifizierung.

Schritt 2 ermöglicht benutzerdefinierte Eingabefelder (die beim Einrichten der Verbindung erfasst werden). Client-ID und Client-Secret werden in Schritt 4 eingegeben, daher deckt dies nur zusätzliche Parameter ab.

**OAuth-Client erstellen**

**Richte einen OAuth-Client**/App/Projekt in deiner Zielanwendung ein und **aktiviere die erforderlichen APIs**. Dies ist anwendungsspezifisch, weshalb unsere Oberfläche benutzerdefinierten Code in Schritt 5 unterstützt.

Für Google Calendar: **Erstelle ein Google Service-Konto**, generiere einen neuen Schlüssel, um die `Client-ID` und das Secret zu erhalten, füge sie in Schritt 4 zu Langdock hinzu, speichere die `OAuth Redirect URL` und aktiviere die Google Calendar API.

**Autorisierungs-URL ändern**

Überprüfe die OAuth-Dokumentation für deinen Service und extrahiere die `Autorisierungs-URL`. Normalerweise reicht es aus, die `BASE_URL` in unserer Vorlage zu ändern.

Für Google Calendar:

```
return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${env.CLIENT_ID}&response_type=code&scope=${data.input.scope}&access_type=offline&redirect_uri=${encodeURIComponent(data.input.redirectUrl)}&state=${data.input.state}&prompt=consent`;
```

**Scopes definieren**

Definiere die OAuth-Scopes, die deine Aktionen benötigen. Liste sie komma- oder leerzeichengetrennt gemäß deiner API-Dokumentation auf.

Für Google Calendar (leerzeichengetrennt):

```
https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
```

**Access Token & Refresh Token URL bereitstellen**

Überprüfe die OAuth-Dokumentation deiner API für die `Access Token URL` und `Refresh Token URL`. Normalerweise funktioniert es, die `tokenUrl` in unserer Vorlage zu aktualisieren.

Für Google Calendar:

`const tokenUrl ='https://oauth2.googleapis.com/token';`

**Authentifizierungs-Setup testen**

Stelle einen Test-API-Endpunkt (wie `/me`) bereit, um die Authentifizierung zu verifizieren. Der Rückgabewert dieser Testanfrage kann innerhalb des **OAuth Client Labels** verwendet werden, um die Benennung der hergestellten Verbindungen zu beeinflussen. Du kannst auf den Rückgabewert über `{{data.input}}` zugreifen.

Für Google Calendar: `Google Sheets - {{data.input.useremail.value}}`

Teste, indem du eine Verbindung hinzufügst und verifizierst, dass der Autorisierungsablauf funktioniert.

Für Google Calendar testen wir mit:

```
url: 'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses'
```

### Öffentliche APIs

Wähle `Keine` für öffentlich verfügbare APIs ohne Authentifizierung.

## Aktionen erstellen

Aktionen ermöglichen es Agenten, mit deinen API-Endpunkten zu interagieren. Es gibt zwei Arten von Aktionen:

* **Reguläre Aktionen**: Standard-API-Interaktionen (Erstellen, Lesen, Aktualisieren, Löschen)
* **Native Aktionen**: Spezielle Dateisuche- und Download-Aktionen, die in das Langdock-Dateisystem integriert sind

### Reguläre Aktionen

Reguläre Aktionen sind der häufigste Typ und verarbeiten Standard-API-Operationen.

#### Wann du reguläre Aktionen erstellen solltest

* **CRUD-Operationen**: Daten über API-Aufrufe erstellen, lesen, aktualisieren oder löschen
* **Datenverarbeitung**: Daten zur Analyse, Transformation oder Validierung an APIs senden
* **Dateioperationen**: Dateien zu Services hochladen, Dokumente verarbeiten, Anhänge senden
* **Benachrichtigungen**: E-Mails, Nachrichten senden oder Tickets erstellen
* **Integrationen**: Mehrere Services verbinden oder Daten zwischen Plattformen synchronisieren

#### Reguläre Aktionen einrichten

1. **Aktion hinzufügen**: Klicke in deiner Integration auf "Aktion hinzufügen"
2. **Grundlegende Informationen konfigurieren**: Name, Beschreibung und Slug festlegen
3. **Eingabefelder hinzufügen**: Definiere, welche Daten die Aktion von Nutzern benötigt
4. **Aktionscode schreiben**: Implementiere die API-Interaktionslogik
5. **Testen**: Validiere, dass deine Aktion korrekt funktioniert

#### Eingabefeldtypen

| Typ               | Zweck              | Hinweise                                                                                                                           |
| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `TEXT`            | Kurze Texteingabe  | Einzeiliger Text                                                                                                                   |
| `MULTI_LINE_TEXT` | Lange Texteingabe  | Mehrere Zeilen, gut für Beschreibungen                                                                                             |
| `NUMBER`          | Numerische Eingabe | Ganze Zahlen oder Dezimalzahlen                                                                                                    |
| `BOOLEAN`         | Wahr/Falsch-Toggle | Checkbox-Eingabe                                                                                                                   |
| `SELECT`          | Dropdown-Optionen  | Vordefinierte Auswahlmöglichkeiten                                                                                                 |
| `FILE`            | Datei-Upload       | Einzelne oder mehrere Dateien ([siehe Dateiunterstützungs-Guide](/de/using-langdock/guides/integrations/file-support-for-actions)) |
| `OBJECT`          | Komplexe Daten     | JSON-Objekte mit benutzerdefiniertem Schema                                                                                        |
| `PASSWORD`        | Sensibler Text     | Versteckte Eingabe für Geheimnisse                                                                                                 |

#### Beispiel: Ticket-Erstellungsaktion

```javascript theme={null}
// Erforderliche Eingaben validieren
if (!data.input.title) {
  return { error: 'Titel ist erforderlich' };
}

// Anfrage erstellen
const options = {
  method: 'POST',
  url: 'https://api.ticketing-service.com/tickets',
  headers: {
    'Authorization': `Bearer ${data.auth.api_key}`,
    'Content-Type': 'application/json',
  },
  body: {
    title: data.input.title,
    description: data.input.description || '',
    priority: data.input.priority || 'medium',
    assignee: data.input.assignee
  }
};

try {
  const response = await ld.request(options);

  if (response.status === 201) {
    return {
      success: true,
      ticketId: response.json.id,
      url: response.json.url,
      message: `Ticket #${response.json.id} erstellt: ${data.input.title}`
    };
  } else {
    throw new Error(`API hat Status ${response.status} zurückgegeben`);
  }
} catch (error) {
  ld.log('Fehler beim Erstellen des Tickets:', error.message);
  return {
    success: false,
    error: `Ticket konnte nicht erstellt werden: ${error.message}`
  };
}
```

#### Datei-Upload-Beispiel

```javascript theme={null}
// Datei-Uploads verarbeiten (erfordert FILE-Eingabefeld)
const document = data.input.document; // FileData-Objekt

if (!document) {
  return { error: 'Bitte hänge ein Dokument an' };
}

// Dateityp validieren
const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png'];
if (!allowedTypes.includes(document.mimeType)) {
  return {
    error: `Nicht unterstützter Dateityp: ${document.mimeType}. Erlaubt: ${allowedTypes.join(', ')}`
  };
}

const options = {
  method: 'POST',
  url: 'https://api.example.com/documents',
  headers: {
    'Authorization': `Bearer ${data.auth.api_key}`,
    'Content-Type': 'application/json'
  },
  body: {
    filename: document.fileName,
    content: document.base64,
    mimeType: document.mimeType
  }
};

const response = await ld.request(options);
return {
  success: true,
  documentId: response.json.id,
  message: `${document.fileName} erfolgreich hochgeladen`
};
```

#### Dateien von Aktionen zurückgeben

Aktionen können auch Dateien generieren und zurückgeben:

```javascript theme={null}
// CSV-Export generieren
const data = await fetchCustomerData();

const csvHeader = 'Name,Email,Erstellt';
const csvRows = data.map(customer =>
  `"${customer.name}","${customer.email}","${customer.created}"`
);
const csvContent = [csvHeader, ...csvRows].join('\n');

return {
  files: {
    fileName: `kunden-${new Date().toISOString().slice(0,10)}.csv`,
    mimeType: 'text/csv',
    text: csvContent  // Verwende 'text' für UTF-8-Inhalt, 'base64' für binär
  },
  success: true,
  exported: data.length
};
```

## Trigger erstellen

Trigger überwachen externe Systeme auf Ereignisse und können Workflows automatisch starten.

### Wann du Trigger erstellen solltest

* **Event-Überwachung**: Neue E-Mails, Dateien, Datensätze oder Änderungen erkennen
* **Workflow-Automatisierung**: Prozesse starten, wenn bestimmte Ereignisse auftreten
* **Datensynchronisierung**: Systeme durch Erkennung von Änderungen synchron halten
* **Benachrichtigungen**: Auf externe Ereignisse reagieren und Nutzer benachrichtigen

### Trigger-Typen

* **Polling-Trigger**: APIs regelmäßig auf neue Ereignisse überprüfen
* **Webhook-Trigger**: Echtzeit-Benachrichtigungen von externen Systemen empfangen

### Polling-Trigger einrichten

1. **Trigger hinzufügen**: Klicke in deiner Integration auf "Trigger hinzufügen"
2. **Einstellungen konfigurieren**: Name, Beschreibung und Polling-Intervall festlegen
3. **Eingabefelder hinzufügen**: Konfigurationsparameter definieren (optional)
4. **Trigger-Code schreiben**: Die Polling-Logik implementieren
5. **Testen**: Validiere, dass dein Trigger Ereignisse korrekt erkennt

#### Erforderliches Rückgabeformat

Trigger müssen ein Array von Ereignissen mit dieser Struktur zurückgeben:

```javascript theme={null}
return [
  {
    id: "unique_event_id",        // Required: Unique identifier
    timestamp: "2024-01-15T...",  // Required: Event timestamp (ISO string)
    data: {
      // Your event data here
      eventType: "new_email",
      subject: "Important message",
      from: "sender@example.com",
      // ... other event properties
    }
  }
];
```

#### Beispiel: Neue E-Mail-Trigger

```javascript theme={null}
// Fetch recent emails
const options = {
  method: 'GET',
  url: 'https://api.email-service.com/messages',
  headers: {
    'Authorization': `Bearer ${data.auth.access_token}`,
  },
  params: {
    since: new Date(Date.now() - 60 * 60 * 1000).toISOString(), // Last hour
    limit: 10
  }
};

try {
  const response = await ld.request(options);
  const emails = response.json.messages || [];

  // Transform to required format
  const results = emails.map(email => ({
    id: email.id,
    timestamp: email.receivedAt,
    data: {
      messageId: email.id,
      subject: email.subject,
      from: email.from,
      to: email.to,
      body: email.body,
      isRead: email.isRead
    }
  }));

  return results;
} catch (error) {
  ld.log('Error fetching emails:', error.message);
  throw error;
}
```

#### Trigger mit Dateianhängen

Wenn Trigger Ereignisse mit Dateien erkennen, füge sie in das Datenobjekt ein:

```javascript theme={null}
const results = [];

for (const email of emails) {
  const attachments = [];

  if (email.attachments && email.attachments.length > 0) {
    for (const attachment of email.attachments) {
      const fileResponse = await ld.request({
        method: "GET",
        url: `https://api.email-service.com/attachments/${attachment.id}`,
        headers: {
          Authorization: `Bearer ${data.auth.access_token}`,
        },
        responseType: "stream",
      });

      attachments.push({
        fileName: attachment.filename,
        mimeType: attachment.mimeType,
        base64: Buffer.from(fileResponse.buffer).toString("base64"),
      });
    }
  }

  results.push({
    id: email.id,
    timestamp: email.receivedAt,
    data: {
      subject: email.subject,
      from: email.from,
      body: email.body,
      files: attachments,
    },
  });
}

return results;
```

### Webhook-Trigger

<Info>
  Der Trigger-Builder unterstützt aktuell nur **Polling-Trigger**. Webhook-Trigger (REST\_HOOK) sind über die API verfügbar, aber noch nicht im Trigger-Builder-UI sichtbar.
</Info>

## Native Aktionen

Native Aktionen ermöglichen es dir, Dateien nativ zu suchen und herunterzuladen, die nicht lokal auf dem Gerät eines Nutzers gespeichert sind. Wir haben bereits native Aktionen für SharePoint, OneDrive, Google Drive und Confluence erstellt. Du kannst über den Button **Dateien auswählen** darauf zugreifen, um Dateien direkt im Chat oder im Agenten-Wissen zu suchen und anzuhängen.

<img src="https://mintcdn.com/langdock-34/yFkcGlsUCYIpoHgV/images/native_actions_chat.png?maxW=1604&auto=format&n=yFkcGlsUCYIpoHgV&q=85&s=81a00e4dccced1be014bf1b75a8d06ba" alt="Native Actions Chat " title="Native Actions Chat " style={{ width:"94%" }} width="1604" height="632" data-path="images/native_actions_chat.png" />

*Dateien mit nativen Integrationen an den Chat anhängen.*

<img src="https://mintcdn.com/langdock-34/yFkcGlsUCYIpoHgV/images/native_actions_knowledge.png?maxW=1412&auto=format&n=yFkcGlsUCYIpoHgV&q=85&s=74a390dafef6008a8e230cdbfe5ae25f" alt="Native Actions Knowledge " title="Native Actions Knowledge " style={{ width:"94%" }} width="1412" height="604" data-path="images/native_actions_knowledge.png" />

*Dateien mit einer nativen Integration an das Agenten-Wissen anhängen.*

Das Erstellen nativer Aktionen für andere Tools ermöglicht es dir, Dateien von diesen Plattformen auf die gleiche Weise zu suchen und herunterzuladen.

#### Eine native Aktion einrichten

Um eine native Aktion einzurichten, beginne wie gewohnt mit dem Erstellen deiner Integration. Füge eine weitere Aktion hinzu und wähle in **Schritt 1** unter Erweitert entweder "**Dateien suchen**" oder "**Datei herunterladen**" als Aktionstyp.

Danach erstellst du die Aktion wie jede andere Aktion, aber deine Funktion muss eine spezifische Objektstruktur zurückgeben. Dies gewährleistet Kompatibilität und ermöglicht es Agenten, Dateien und Suchergebnisse korrekt zu verarbeiten.

#### Erforderliches Ausgabeformat

Abhängig von der ausgewählten Aktion muss deine Funktion eine spezifische Objektstruktur zurückgeben. Dies gewährleistet Kompatibilität und ermöglicht es Agenten, Dateien und Suchergebnisse korrekt zu verarbeiten.

**Dateien suchen**: Beim Erstellen einer nativen Suchintegration muss deine Funktion ein Array von Objekten zurückgeben, die dem folgenden Schema entsprechen:

```typescript theme={null}
{
  url: string,
  documentId: string,
  title: string,
  author?: {
    id: string,
    name: string,
    imgUrl?: string,
  },
  mimeType: string,
  lastSeenByUser: Date,
  createdDate: Date,
  lastModifiedByAnyone: Date,
  lastModifiedByUserId?: {
    id?: string,
    name?: string,
    lastModifiedByUserIdDate: Date,
  },
  parent?: {
    id: string,
    title?: string,
    url?: string,
    type?: string,
    driveId?: string,
    siteId?: string,
    listId?: string,
    listItemId?: string,
  }
}
```

Der *title* und *mimeType* werden in der Nutzeroberfläche für alle Suchergebnisse angezeigt.

Bitte sieh dir auch eine detaillierte Beschreibung für jeden Parameter an:

<Expandable title="Parameterbeschreibungen">
  ### **Erforderliche Felder**

  | Feld       | Typ    | Beschreibung                                                                                                                                                                                                                                                                                           |
  | :--------- | :----- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | url        | string | **Die Web-URL, unter der Nutzer die Datei anzeigen oder bearbeiten können.** Dies sollte ein direkter Link sein, der die Datei in der Quellanwendung öffnet (z.B. Google Docs Editor, SharePoint Viewer). Muss eine gültige HTTPS-URL sein, auf die der Nutzer mit seinen Anmeldedaten zugreifen kann. |
  | documentId | string | **Der eindeutige Identifikator der Datei im Quellsystem.** Diese ID wird intern zur Referenzierung der Datei verwendet und sollte bei Suchen stabil bleiben. Kann jedes String-Format sein (UUID, numerische ID, etc.), solange es die Datei eindeutig identifiziert.                                  |
  | title      | string | **Der Anzeigename der Datei.** Das ist, was Nutzer in den Suchergebnissen sehen werden. Sollte die Dateierweiterung enthalten, falls relevant (z.B. "Bericht.pdf", "Budget.xlsx").                                                                                                                     |
  | mimeType   | string | **Der MIME-Typ der Datei.** Wird verwendet, um das Dateityp-Icon und die Kategorie zu bestimmen (z.B. "application/pdf", "text/plain", "application/vnd.google-apps.document").                                                                                                                        |

  ### **Optionale Felder**

  | Feld                                          | Typ    | Beschreibung                                                                                                                                                                                                                                                                              |
  | :-------------------------------------------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | author                                        | object | **Informationen über denjenigen, der die Datei erstellt hat.** Hilft Nutzern, Dateieigentümerschaft und Herkunft zu identifizieren.                                                                                                                                                       |
  | author.id                                     | string | **Eindeutiger Identifikator des Autors.** Normalerweise eine E-Mail-Adresse oder Nutzer-ID im Quellsystem.                                                                                                                                                                                |
  | author.name                                   | string | **Anzeigename des Autors.** Der für Menschen lesbare Name, der Nutzern angezeigt wird (z.B. "Max Mustermann").                                                                                                                                                                            |
  | lastSeenByUser                                | string | **Wann der aktuelle Nutzer diese Datei zuletzt angesehen hat.** ISO 8601 Datumsstring (z.B. "2024-01-15T10:30:00Z"). Wird für die Sortierung "Kürzlich angesehen" verwendet. Gib null zurück oder lass es weg, wenn der Nutzer die Datei nie angesehen hat.                               |
  | createdDate                                   | string | **Wann die Datei ursprünglich erstellt wurde.** ISO 8601 Datumsstring. Hilft Nutzern, das Alter der Datei zu verstehen und nach Erstellungsdatum zu sortieren.                                                                                                                            |
  | lastModifiedByAnyone                          | string | **Wann die Datei zuletzt von irgendeinem Nutzer geändert wurde.** ISO 8601 Datumsstring. Wichtig zur Identifikation kürzlich aktualisierter Inhalte und kollaborativer Arbeit.                                                                                                            |
  | lastModifiedByUserId                          | object | **Informationen darüber, wer die Datei zuletzt geändert hat.** Hilft beim Verfolgen kürzlicher Änderungen in kollaborativen Umgebungen. Das gesamte Objekt sollte weggelassen werden, wenn ein erforderliches Unterfeld fehlt.                                                            |
  | lastModifiedByUserId.id                       | string | **Eindeutiger Identifikator des letzten Bearbeiters.** Normalerweise eine E-Mail oder Nutzer-ID.                                                                                                                                                                                          |
  | lastModifiedByUserId.name                     | string | **Anzeigename des letzten Bearbeiters.** Für Menschen lesbarer Name desjenigen, der die letzten Änderungen vorgenommen hat.                                                                                                                                                               |
  | lastModifiedByUserId.lastModifiedByUserIdDate | string | **Zeitstempel der letzten Änderung.** ISO 8601 Datumsstring. Stimmt normalerweise mit lastModifiedByAnyone überein.                                                                                                                                                                       |
  | parent                                        | object | **Informationen über den Speicherort/Container der Datei.** Hilft Nutzern, die Dateiorganisation zu verstehen und zu übergeordneten Ordnern zu navigieren.                                                                                                                                |
  | parent.id                                     | string | **Eindeutiger Identifikator des übergeordneten Ordners/Containers.** Wird für ordnerbasierte Operationen und Navigation verwendet.                                                                                                                                                        |
  | parent.title                                  | string | **Anzeigename des übergeordneten Ordners.** Wird angezeigt, um Nutzern zu helfen, den Dateistandort zu verstehen (z.B. "Marketing-Materialien", "Q1 Berichte").                                                                                                                           |
  | parent.url                                    | string | **Web-URL zum Anzeigen des übergeordneten Ordners.** Direkter Link zum Öffnen des Ordners in der Quellanwendung.                                                                                                                                                                          |
  | parent.type                                   | string | **Typ des übergeordneten Containers.** Optionale Klassifizierung (z.B. "folder", "workspace", "site").                                                                                                                                                                                    |
  | parent.driveId                                | string | **Identifikator des Laufwerks/der Bibliothek, die die Datei enthält.** Für Services mit mehreren Speicherorten (z.B. SharePoint-Sites, Google Shared Drives).                                                                                                                             |
  | parent.siteId                                 | string | **Identifikator der Site, die die Datei enthält.** Spezifisch für SharePoint und ähnliche Plattformen mit sitebasierter Organisation.                                                                                                                                                     |
  | parent.listId                                 | string | **Identifikator der Liste, die die Datei enthält.** Für listenbasierte Speichersysteme.                                                                                                                                                                                                   |
  | parent.listItemId                             | string | **Identifikator des Listenelements, das mit der Datei verbunden ist.** Für Dateien, die an Listenelemente angehängt sind.                                                                                                                                                                 |
  | contentPreview                                | string | **Ein Textausschnitt aus dem Dateiinhalt.** Bietet Kontext über Dateiinhalte in Suchergebnissen. Sollte Klartext sein, normalerweise 100-200 Zeichen. Nützlich zum Anzeigen relevanter Ausschnitte, die zu Suchanfragen passen. Setze auf null, wenn keine Inhaltsvorschau verfügbar ist. |

  ### **Nutzungsrichtlinien**

  **Datum**: \
  Alle Datumsfelder müssen gültige ISO 8601 Strings sein oder vollständig weggelassen werden. Ungültige Daten verursachen Parsing-Fehler.

  **Null vs. Weggelassen**:

  * Verwende null für Felder, die explizit leer sind (z.B. keine Inhaltsvorschau verfügbar)
  * Lass Felder vollständig weg, wenn die Daten nicht anwendbar oder nicht verfügbar sind

  **Übergeordnete Informationen**: Füge so viele übergeordnete Informationen wie verfügbar hinzu, um Nutzern bei der Navigation in Dateihierarchien zu helfen

  * **Autor-Informationen**: Füge immer sowohl ID als auch Name in Autor-Objekten hinzu, oder lass das gesamte Objekt weg
  * **Suchrelevanz**: Felder wie contentPreview können die Such-UX erheblich verbessern, indem sie zeigen, warum eine Datei zur Anfrage passt
</Expandable>

Below you can find an example implementation for the native SharePoint *Search files* action.

<Expandable title="SharePoint-Beispiel">
  ```typescript theme={null}
  const entityTypes = ["driveItem"];
  const queryString = data.input.query;

  try {
    // Suche durchführen, wenn Anfrage existiert
    const searchRequest = {
      requests: [
        {
          entityTypes,
          query: { queryString },
          trimDuplicates: true,
          queryAlterationOptions: {
            enableModification: true,
            enableSuggestions: true,
          },
        },
      ],
    };

    const searchResult = await ld.request({
      method: 'POST',
      url: 'https://graph.microsoft.com/v1.0/search/query',
      body: searchRequest,
      headers: {
        'Authorization': `Bearer ${data.auth.access_token}`,
        'Content-Type': 'application/json',
      },
    });

    const hits = searchResult.json?.value?.[0]?.hitsContainers?.[0]?.hits;
    if (hits && hits.length > 0) {
      const results = hits.filter((hit) => hit.resource.name).map((hit) => {
        const { resource } = hit;
        return {
          url: encodeURI(`${resource.webUrl}?web=1`),
          documentId: resource.id,
          title: resource.name,
          mimeType: getMimeTypeFromFileName(resource.name),
          author: resource.createdBy?.user ? {
            id: resource.createdBy.user.email || '',
            name: resource.createdBy.user.displayName || '',
          } : undefined,
          createdDate: resource.createdDateTime,
          lastModifiedByAnyone: resource.lastModifiedDateTime,
          lastModifiedByUserId: resource.lastModifiedBy?.user ? {
            id: resource.lastModifiedBy.user.email || '',
            name: resource.lastModifiedBy.user.displayName || '',
            lastModifiedByUserIdDate: resource.lastModifiedDateTime,
          } : undefined,
          parent: resource.parentReference ? {
            id: resource.parentReference.id || '',
            title: resource.parentReference.path ? resource.parentReference.path.split('/').pop() : undefined,
            driveId: resource.parentReference.driveId || '',
          } : undefined,
        };
      });
      return results;
    }
    return [];
  } catch (error) {
    ld.log(`Error: ${error.message}, Stack: ${error.stack}`);
    return [];
  }

  function getMimeTypeFromFileName(fileName) {
    const extension = fileName.split('.').pop().toLowerCase();
    const mimeTypes = {
      'txt': 'text/plain',
      'doc': 'application/msword',
      'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'pdf': 'application/pdf',
    };
    return mimeTypes[extension] || 'application/octet-stream';
  }
  ```
</Expandable>

**Download file**\
For native download actions, return an object in the following format:

```typescript theme={null}
{
  fileName: string,
  mimeType: string,
  buffer: response.buffer, // Bedingt: für Binärdateien
  url: string,
  lastModified: Date,
  text: string // Bedingt: für Textdateien
}
```

Der *fileName* und *mimeType* werden in der Nutzeroberfläche für alle Suchergebnisse angezeigt.

Bitte sieh dir auch eine detaillierte Beschreibung für jeden Parameter an:

<Expandable title="Parameterbeschreibungen">
  ### Übersicht

  | Feld         | Typ         | Erforderlich | Beschreibung                                                                                                                                                                                                                                                                                                                                    |
  | :----------- | :---------- | :----------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | fileName     | string      | **Ja**       | **Der vollständige Dateiname inklusive Erweiterung.** Das ist der Name, der beim Speichern der Datei verwendet wird. Sollte dem ursprünglichen Dateinamen aus dem Quellsystem entsprechen (z.B. "Budget\_2024.xlsx", "Design\_Final.pdf"). Wenn die Datei im Quellsystem keine Erweiterung hat, füge die passende basierend auf mimeType hinzu. |
  | mimeType     | string      | **Ja**       | **Der MIME-Typ, der das Dateiformat identifiziert.** Bestimmt, wie die Datei verarbeitet wird und welches Icon angezeigt wird. Muss ein gültiger MIME-Typ sein (z.B. "application/pdf", "image/png", "text/plain"). Für Google Workspace-Dateien verwende den ursprünglichen MIME-Typ, nicht den des exportierten Formats.                      |
  | buffer       | Buffer      | **Bedingt**  | **Der binäre Inhalt der Datei als Buffer.** Erforderlich für Binärdateien (PDFs, Bilder, Office-Dokumente). Die eigentlichen Dateidaten, die gespeichert werden. Kann bereitgestellt werden als: natives Buffer-Objekt, base64-kodierter String oder Objekt mit Format type:"Buffer", data: \[Byte-Array].                                      |
  | url          | string      | **Ja**       | **Die Web-URL zum Anzeigen/Bearbeiten der Datei in ihrer Quellanwendung.** Sollte ein direkter Link sein, der die Datei beim Klicken öffnet (z.B. Google Drive Viewer URL, SharePoint Dokument-URL). Wird verwendet, damit Nutzer auf die ursprüngliche Datei zugreifen können und zur Referenzverfolgung.                                      |
  | lastModified | string/Date | **Ja**       | **ISO 8601 Zeitstempel der letzten Änderung der Datei.** Zeigt an, wann der Dateiinhalt zuletzt geändert wurde. Kann ein Date-Objekt oder ISO-String wie "2024-01-15T10:30:00Z" sein. Wird für Versionsverfolgung und Bestimmung der Dateiaktualität verwendet.                                                                                 |
  | text         | string      | **Bedingt**  | **Der Textinhalt der Datei als UTF-8-String.** Erforderlich für textbasierte Dateien anstelle von buffer. Verwende für Klartext, HTML, JSON, CSV oder jedes für Menschen lesbare Format. Sollte den vollständigen Dateiinhalt enthalten. Kann nicht zusammen mit buffer verwendet werden.                                                       |

  ### **Wichtige Hinweise**

  **Inhaltsfelder**: Du musst entweder buffer ODER text bereitstellen, niemals beides:\
  Verwende buffer für: Bilder, PDFs, Office-Dokumente, Videos, jedes Binärformat\
  Verwende text für: Klartext, HTML, Quellcode, JSON, XML, jedes Textformat

  **MIME-Typ-Genauigkeit**: Der mimeType muss genau den zurückgegebenen Inhalt widerspiegeln:

  * Für native Google Docs, die als HTML exportiert werden, verwende trotzdem "application/vnd.google-apps.document"
  * Für konvertierte Dateien verwende den ursprünglichen Quell-MIME-Typ, nicht den des Exportformats

  **Dateibenennung**: Der fileName sollte:

  * Die korrekte Dateierweiterung enthalten
  * Dem entsprechen, was Nutzer vom Quellsystem erwarten
  * Bereinigt sein, um ungültige Dateisystemzeichen zu entfernen

  **URL-Anforderungen**: Die url muss:

  * Mit der Authentifizierung des Nutzers zugänglich sein
  * Die Datei in der Quellanwendung öffnen (kein Download-Link)
  * Ein stabiler Link sein, der nicht schnell abläuft
</Expandable>

Below you can find an example implementation for the native SharePoint *Download file* action.

<Expandable title="SharePoint-Beispiel">
  ```typescript theme={null}
  async function downloadOneDriveFile() {
    try {
      // API-Pfad basierend auf der Eingabekonfiguration erstellen
      const config = JSON.parse(data.input.parent);
      let apiPath = '';

      if (config.listId && config.listItemId) {
        apiPath = `/sites/${config.siteId}/lists/${config.listId}/items/${data.input.itemId}/driveItem`;
      } else if (config.driveId) {
        apiPath = `/drives/${config.driveId}/items/${data.input.itemId}`;
      } else if (config.groupId) {
        apiPath = `/groups/${config.groupId}/drive/items/${data.input.itemId}`;
      } else if (config.userId) {
        apiPath = `/users/${config.userId}/drive/items/${data.input.itemId}`;
      } else if (config.siteId) {
        apiPath = `/sites/${config.siteId}/drive/items/${data.input.itemId}`;
      } else {
        throw new Error('Unzureichende Informationen zum Erstellen des API-Pfads');
      }

      // Anfrage stellen, um Dateimetadaten inklusive Download-URL zu erhalten
      const options = {
        method: 'GET',
        url: `https://graph.microsoft.com/v1.0${apiPath}`,
        headers: {
          'Authorization': 'Bearer ' + data.auth.access_token,
          'Accept': 'application/json',
        },
      };

      const response = await ld.request(options);

      if (response.json['@microsoft.graph.downloadUrl']) {
        const downloadUrl = response.json['@microsoft.graph.downloadUrl'];

        // Anfrage zum Herunterladen des Dateiinhalts
        const contentOptions = {
          method: 'GET',
          url: downloadUrl,
          responseType: 'stream'
        };

        const contentResponse = await ld.request(contentOptions);

        if (contentResponse.status !== 200) {
          throw new Error(
            `Fehler beim Abrufen des Dateiinhalts: ${JSON.stringify(contentResponse)}`
          );
        }

        return {
          fileName: response.json.name,
          mimeType: response.json.file.mimeType,
          buffer: contentResponse.buffer,
          url: response.json.webUrl,
          lastModified: response.json.lastModifiedDateTime,
        };
      } else {
        throw new Error('Datei konnte nicht heruntergeladen werden!');
      }
    } catch (error) {
      ld.log('Fehler beim Herunterladen des Elements aus OneDrive: ' + error.message);
      throw error;
    }
  }

  return downloadOneDriveFile();
  ```
</Expandable>

## Auf Eingabefelder zugreifen

Verwende `data.input.{fieldSlug}` für Eingabefeldwerte und `data.auth.{fieldSlug}` für Authentifizierungsfeldwerte aus der aktuellen Verbindung des Nutzers. Der Slug ist der Bezeichner, den du beim Erstellen jedes Feldes im Integrations-Builder festlegst.

## Integrierte Funktionen für benutzerdefinierte Code-Abschnitte

<Tip>
  Verwende unseren [Integrations-Agent](/de/using-langdock/guides/integrations/agent), um dir beim Einrichten deiner Integrationsfunktionen zu helfen.
</Tip>

Benutzerdefinierte Code-Abschnitte haben Zugriff auf eine Reihe von integrierten Utility-Funktionen für gängige Operationen. Hier sind die am häufigsten verwendeten:

### Wesentliche Funktionen

* **`ld.request()`** - HTTP-Anfragen an externe APIs ausführen
* **`ld.log()`** - Debugging-Informationen ausgeben
* **`atob()` / `btoa()`** - Base64-Encoding/-Decoding
* **`JSON.stringify()` / `JSON.parse()`** - JSON-Manipulation

<Card title="Vollständige Sandbox-Utilities-Referenz" icon="code" href="/de/using-langdock/guides/integrations/sandbox-utilities">
  Sieh dir alle verfügbaren Sandbox-Utilities an, einschließlich Datenkonvertierungen (CSV, Parquet, Arrow), SQL-Validierung, Kryptographie, AWS-Request-Signing, Microsoft XMLA-Integration und mehr.
</Card>

### Schnelle Beispiele

**HTTP-Anfrage**

```javascript theme={null}
const options = {
  method: "GET",
  url: `https://www.googleapis.com/calendar/v3/calendars/${data.input.calendarId}/events/${data.input.eventId}`,
  headers: {
    Authorization: "Bearer " + data.auth.access_token,
    Accept: "application/json",
  },
};

const response = await ld.request(options);
return response.json;
```

**JSON-Parsing**

```javascript theme={null}
const properties = data.input.properties
  ? JSON.parse(data.input.properties)
  : {};

const options = {
  method: "PATCH",
  url: `https://api.hubapi.com/crm/v3/objects/companies/${data.input.companyId}`,
  headers: {
    Authorization: "Bearer " + data.auth.access_token,
    "Content-Type": "application/json",
  },
  body: { properties },
};
```

**Base64-Encoding**

```javascript theme={null}
const auth = btoa(`${env.CLIENT_ID}:${env.CLIENT_SECRET}`);
```

### Sandbox-Bibliotheksbeschränkungen

> Nutzerdefinierter Integrationscode läuft in einer sicheren Sandbox-Umgebung.
> **Du kannst keine externen Bibliotheken installieren oder importieren (npm, pip usw.) – nur eine begrenzte Anzahl integrierter JavaScript/Node.js-APIs ist verfügbar.**
> Für erweiterte Verarbeitung (z.B. PDF-Parsing, Bildbearbeitung) verwende externe APIs oder Services und rufe sie aus deinem Integrationscode auf.

## Best Practices

### Aktionsdesign

* **Einzelne Verantwortlichkeit**: Jede Aktion sollte eine Sache gut machen
* **Klare Benennung**: Verwende beschreibende Aktionsnamen, die den Zweck erklären
* **Eingabevalidierung**: Validiere immer erforderliche Eingaben und gib hilfreiche Fehlermeldungen aus
* **Fehlerbehandlung**: Fange API-Fehler ab und behandle sie angemessen
* **Protokollierung**: Verwende `ld.log()` zur Unterstützung beim Debugging

### ID-Verwaltung

Die meisten API-Aufrufe erfordern spezifische interne IDs. Die Herausforderung besteht darin, dass Agenten diese IDs nicht erraten können, was zu einer schlechten Nutzererfahrung führt, wenn Aktionen wie "spezifischen Kontakt in HubSpot abrufen" oder "Ereignis zu spezifischem Kalender in Google Calendar hinzufügen" aufgerufen werden.

Die Lösung: Erstelle Hilfsaktionen, die diese IDs zuerst abrufen und an den Agenten zurückgeben. Beispielsweise verwendet unsere Funktion `Deal-Kontext abrufen` für HubSpot GET-Endpunkte, um interne IDs für verfügbare Pipelines und Phasen zu sammeln. Dies ermöglicht es Agenten, Aktionen wie `Deal erstellen` oder `Deal aktualisieren` viel effektiver zu nutzen, da sie nun den erforderlichen Kontext haben.

### Performance

* **API-Aufrufe minimieren**: Batch-Operationen verwenden, wenn möglich
* **Paginierung verwenden**: Große Datensätze angemessen verarbeiten
* **Timeout-Behandlung**: Angemessene Timeouts für externe API-Aufrufe setzen

### Sicherheit

* **Eingaben validieren**: Vertraue niemals Nutzereingaben ohne Validierung
* **Daten bereinigen**: Bereinige Daten, bevor du sie an externe APIs sendest
* **Geheimnisse verwalten**: Verwende Authentifizierungsfelder für sensible Daten, niemals hartcodieren
* **Rate Limiting**: Respektiere API-Rate-Limits und implementiere Backoff-Strategien

***

## FAQ

<AccordionGroup>
  <Accordion title="Wann sollte ich eine benutzerdefinierte Integration erstellen?">
    Erstelle eine benutzerdefinierte Integration, wenn Langdock mit einem Tool oder einer API verbunden werden soll, die keine bestehende Integration abdeckt. Sie eignet sich am besten für wiederholbare Aktionen, den Abruf strukturierter Daten und Workflows, bei denen Nutzer Daten nicht manuell zwischen Systemen kopieren sollen.
  </Accordion>

  <Accordion title="Was sollte ich prüfen, wenn eine benutzerdefinierte Integration nicht wie erwartet funktioniert?">
    Prüfe die Authentifizierung, die Endpunkt-URLs, die Request-Methoden, erforderliche Header, das Eingabeschema, das Ausgabeschema und ob die externe API die erwartete Antwort liefert. Teste zuerst eine einzelne Aktion mit einfachen Eingaben, bevor du komplexe Logik hinzufügst.
  </Accordion>
</AccordionGroup>
