--- title: Verbindungen description: Ermöglichen Sie Ihrer App, im Namen eines Benutzers über OAuth in Diensten von Drittanbietern zu handeln. icon: plug --- Verbindungen sind Anmeldedaten, die ein Benutzer für einen externen Dienst besitzt (Linear, GitHub, Slack, ...). Ihre App legt fest, **wie** diese Anmeldedaten bezogen werden — ein **Verbindungsanbieter** — und verwendet sie zur Laufzeit, um authentifizierte Aufrufe an die Drittanbieter-API zu tätigen. Derzeit wird nur OAuth 2.0 unterstützt. Zukünftige Anmeldedatentypen (Personal Access Tokens, API-Schlüssel, Basic Auth) werden in dieselbe Oberfläche integriert — Apps, die bereits `defineConnectionProvider({ type: 'oauth', ... })` müssen nicht migriert werden. Ein Verbindungsanbieter beschreibt den OAuth-Handshake, den Ihre App benötigt. Der Benutzer klickt in den Einstellungen Ihrer App auf "Verbindung hinzufügen", schließt den Zustimmungsbildschirm des Anbieters ab, und in seinem Arbeitsbereich wird eine `ConnectedAccount`-Zeile erstellt. Eine funktionierende Einrichtung benötigt **zwei Dateien** — den Verbindungsanbieter und eine passende `serverVariables`-Deklaration in `defineApplication`, die die OAuth-Client-Anmeldedaten enthält. ```ts src/connection-providers/linear-connection.ts import { defineConnectionProvider } from 'twenty-sdk/define'; export default defineConnectionProvider({ universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f', name: 'linear', displayName: 'Linear', icon: 'IconBrandLinear', type: 'oauth', oauth: { authorizationEndpoint: 'https://linear.app/oauth/authorize', tokenEndpoint: 'https://api.linear.app/oauth/token', scopes: ['read', 'write'], // These must match keys in `defineApplication.serverVariables` below. clientIdVariable: 'LINEAR_CLIENT_ID', clientSecretVariable: 'LINEAR_CLIENT_SECRET', // Optional: defaults to 'json'. Some providers (Linear, Slack) want // 'form-urlencoded' for the token request. tokenRequestContentType: 'form-urlencoded', // Optional: defaults to true. Disable only if the provider rejects PKCE. usePkce: false, // Optional: extra query params on the authorize URL. // authorizationParams: { prompt: 'consent' }, // Optional: provider's RFC 7009 token revocation endpoint, called on disconnect. // revokeEndpoint: 'https://example.com/oauth/revoke', }, }); ``` ```ts src/application.config.ts import { defineApplication } from 'twenty-sdk/define'; export default defineApplication({ universalIdentifier: '...', displayName: 'Linear', description: 'Connect Linear to Twenty.', defaultRoleUniversalIdentifier: '...', // OAuth client credentials live on the app registration (one OAuth app per // Twenty server, configured by the admin) — not per-workspace. Declare them // as serverVariables so the admin can fill them in once for all installs. serverVariables: { LINEAR_CLIENT_ID: { description: 'OAuth client ID from your Linear OAuth application.', isSecret: false, isRequired: true, }, LINEAR_CLIENT_SECRET: { description: 'OAuth client secret from your Linear OAuth application.', isSecret: true, isRequired: true, }, }, }); ``` Hauptpunkte: * `name` ist die eindeutige Bezeichner-Zeichenfolge, die in `listConnections({ providerName })` verwendet wird (kebab-case, muss `^[a-z][a-z0-9-]*$` entsprechen). * `displayName` wird im Einstellungs-Tab der jeweiligen App und in der KI-Toolliste angezeigt. * `clientIdVariable` / `clientSecretVariable` sind **Namen**, keine Werte — sie müssen den in `defineApplication.serverVariables` deklarierten Schlüsseln entsprechen. Die tatsächlichen `client_id` und `client_secret` werden vom Serveradministrator über die App-Registrierungsoberfläche eingegeben und niemals in Ihr Repository eingecheckt. * Verwenden Sie `serverVariables` (nicht `applicationVariables`) — OAuth-Anmeldedaten gelten serverweit und es gibt eine OAuth-App pro Twenty-Server. * Solange beide `serverVariables` nicht ausgefüllt sind, zeigt der Einstellungs-Tab pro App den Hinweis "Benötigt Server-Admin" an und der Button "Verbindung hinzufügen" ist deaktiviert. * `type: 'oauth'` ist derzeit der einzige unterstützte Wert. Der Diskriminator ist vorwärtskompatibel: zukünftige Typen (`'pat'`, `'api-key'`, ...) werden neue Unterkonfigurationsblöcke neben `oauth` hinzufügen. Die OAuth-Callback-URL, die Ihr Anbieter auf die Whitelist setzen muss, lautet: ``` https:///apps/oauth/callback ``` Innerhalb eines Logikfunktions-Handlers gibt `listConnections({ providerName })` die `ConnectedAccount`-Zeilen dieser App für den angegebenen Anbieter zurück, mit aktualisierten Zugriffstoken. ```ts src/logic-functions/handlers/create-linear-issue-handler.ts import { listConnections } from 'twenty-sdk/logic-function'; export const createLinearIssueHandler = async (input: { teamId?: string; title?: string; }) => { if (!input.teamId || !input.title) { return { success: false, error: 'teamId and title are required' }; } const connections = await listConnections({ providerName: 'linear' }); // Workspace-shared credentials win when present; fall back to the first // user-visibility one. For HTTP-route triggers you typically pick the // request user's connection via event.userWorkspaceId instead. const connection = connections.find((c) => c.visibility === 'workspace') ?? connections[0]; if (!connection) { return { success: false, error: 'Linear is not connected. Open the app settings and click "Add connection".', }; } // Use connection.accessToken to call the third-party API. const response = await fetch('https://api.linear.app/graphql', { method: 'POST', headers: { Authorization: `Bearer ${connection.accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`, }), }); return { success: response.ok }; }; ``` Jede Verbindung hat: | Feld | Beschreibung | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | `id` | Eindeutige Zeilen-ID; an `getConnection(id)` übergeben, um eine einzelne Verbindung erneut abzurufen | | `sichtbarkeit` | `'user'` (privat für ein Mitglied des Arbeitsbereichs) oder `'workspace'` (mit allen Mitgliedern geteilt) | | `geltungsbereiche` | Vom Upstream-Anbieter gewährte OAuth-Berechtigungen (unabhängig von `visibility` — diese sind nicht miteinander verknüpft) | | `userWorkspaceId` | Die userWorkspace-ID des Eigentümers — nützlich, um "die Verbindung des anfragenden Benutzers" in HTTP-Routen-Triggern auszuwählen | | `accessToken` | Frisches OAuth-Zugriffstoken (wird bei Ablauf automatisch erneuert) | | `name` / `handle` | Anzeigename der Verbindung (automatisch beim OAuth-Callback abgeleitet, vom Benutzer umbenennbar) | | `authFailedAt` | Gesetzt, wenn die jüngste Aktualisierung fehlgeschlagen ist; der Benutzer muss die Verbindung erneut herstellen | Hauptpunkte: * Übergeben Sie `{ providerName }`, um nach Anbieter zu filtern; lassen Sie es weg, um alle Verbindungen dieser App über alle Anbieter hinweg zu erhalten. * Der Server aktualisiert das Zugriffstoken vor der Rückgabe transparent. Ihr Handler sieht stets ein verwendbares Token (oder `authFailedAt` ist gesetzt). * `getConnection(id)` ist das Pendant für eine einzelne Zeile. Wenn ein Benutzer auf "Verbindung hinzufügen" klickt, wird er aufgefordert, eine Sichtbarkeit auszuwählen: * **Nur für mich** — die Anmeldedaten sind für den sich verbindenden Benutzer privat. Jede Logikfunktion, die in seinem/ihrem Auftrag aufgerufen wird (HTTP-Routen-Trigger mit `isAuthRequired: true`), sieht sie; Cron-Trigger und Datenbankereignisse nicht. * **Im Arbeitsbereich geteilt** — jedes Arbeitsbereichsmitglied kann die Anmeldedaten verwenden. Cron-/Datenbank-Trigger sehen sie ebenfalls, da sie keinen anfragenden Benutzer haben. Verwenden Sie für jeden Handler die richtige Option: ```ts // HTTP-route trigger — prefer the request user's own connection. const conn = connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ?? connections.find((c) => c.visibility === 'workspace'); // Cron trigger — no request user; only shared credentials are sensible. const conn = connections.find((c) => c.visibility === 'workspace'); ``` Mehrere Verbindungen pro (Benutzer, Anbieter) sind erlaubt, sodass derselbe Benutzer "Persönliches Linear" und "Arbeits-Linear" nebeneinander haben kann. Für jeden Verbindungsanbieter muss der Serveradministrator zunächst eine OAuth-App beim Drittanbieter registrieren. 1. Gehen Sie zu den Entwickler-Einstellungen des Anbieters (z. B. https://linear.app/settings/api/applications/new). 2. Setzen Sie die **Redirect-URI** auf `\/apps/oauth/callback`. 3. Kopieren Sie die generierte **Client ID** und das **Client Secret**. 4. Öffnen Sie die installierte App in Twenty als Serveradministrator → setzen Sie die Werte in den entsprechenden `serverVariables`. 5. Mitglieder des Arbeitsbereichs können dann Verbindungen im **Verbindungen**-Abschnitt der jeweiligen App hinzufügen.