Vai al contenuto principale
Le connessioni sono credenziali che un utente detiene per un servizio esterno (Linear, GitHub, Slack, …). La tua app dichiara come vengono ottenute tali credenziali — un provider di connessione — e le utilizza in fase di esecuzione per effettuare chiamate autenticate all’API di terze parti. Oggi è supportato solo OAuth 2.0. Tipi di credenziali futuri (token di accesso personali, chiavi API, autenticazione di base) si integreranno nella stessa interfaccia — le app che già usano defineConnectionProvider({ type: 'oauth', ... }) non dovranno migrare.
Un provider di connessione descrive l’handshake OAuth di cui la tua app ha bisogno. L’utente fa clic su “Aggiungi connessione” nelle impostazioni della tua app, completa la schermata di consenso del provider e viene creata una riga ConnectedAccount nel suo spazio di lavoro.Una configurazione funzionante richiede due file — il provider di connessione e una dichiarazione serverVariables corrispondente su defineApplication che contiene le credenziali client OAuth.
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',
  },
});
src/application.config.ts
import { defineApplication } from 'twenty-sdk/define';

export default defineApplication({
  universalIdentifier: '...',
  displayName: 'Linear',
  description: 'Connect Linear to Twenty.',
  // 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,
    },
  },
});
Punti chiave:
  • name è la stringa identificativa univoca usata in listConnections({ providerName }) (kebab-case, deve corrispondere a ^[a-z][a-z0-9-]*$).
  • displayName viene mostrato nella scheda delle impostazioni per app e nell’elenco degli strumenti AI.
  • clientIdVariable / clientSecretVariable sono nomi, non valori — devono corrispondere alle chiavi dichiarate in defineApplication.serverVariables. I client_id e client_secret effettivi vengono inseriti dall’amministratore del server tramite l’interfaccia di registrazione dell’app e non vengono mai aggiunti al tuo repository.
  • Usa serverVariables (non applicationVariables) — le credenziali OAuth sono a livello di server e c’è una sola app OAuth per ogni server Twenty.
  • Finché entrambe le serverVariables non sono compilate, la scheda delle impostazioni per app mostra un suggerimento “richiede l’amministratore del server” e il pulsante “Aggiungi connessione” è disattivato.
  • type: 'oauth' è l’unico valore supportato oggi. Il discriminatore è compatibile con versioni future: tipi futuri ('pat', 'api-key', …) aggiungeranno nuovi blocchi di sotto-configurazione accanto a oauth.
L’URL di callback OAuth che il tuo provider deve inserire nella whitelist è:
https://<your-twenty-server>/auth/apps/callback
All’interno di un gestore di funzione di logica, listConnections({ providerName }) restituisce le righe ConnectedAccount di questa app per il provider indicato, con token di accesso aggiornati.
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 };
};
Ogni connessione ha:
CampoDescrizione
idID di riga univoco; passalo a getConnection(id) per recuperarne una singola connessione
visibility'user' (privata per un membro dello spazio di lavoro) oppure 'workspace' (condivisa con tutti i membri)
scopesAutorizzazioni OAuth concesse dal provider a monte (distinte da visibility — non sono correlate)
userWorkspaceIdL’id userWorkspace del proprietario — utile per selezionare “la connessione dell’utente della richiesta” nei trigger di route HTTP
accessTokenToken di accesso OAuth aggiornato (rinnovato automaticamente se scaduto)
name / handleIl nome visualizzato della connessione (derivato automaticamente al callback OAuth, rinominabile dall’utente)
authFailedAtImpostato quando l’aggiornamento più recente non è riuscito; l’utente deve riconnettersi
Punti chiave:
  • Passa { providerName } per filtrare per provider; omettilo per ottenere tutte le connessioni possedute da questa app su tutti i provider.
  • Il server rinnova in modo trasparente il token di accesso prima di restituire la risposta. Il tuo handler vede sempre un token utilizzabile (oppure authFailedAt impostato).
  • getConnection(id) è l’equivalente a singola riga.
Quando un utente fa clic su “Aggiungi connessione”, viene richiesto di scegliere una visibilità:
  • Solo per me — la credenziale è privata per l’utente che effettua la connessione. Qualsiasi funzione di logica chiamata per loro conto (trigger di route HTTP con isAuthRequired: true) la vede; i trigger cron e gli eventi del database no.
  • Condivisa nello spazio di lavoro — qualsiasi membro dello spazio di lavoro può usare la credenziale. Anche i trigger cron/database la vedono, poiché non hanno un utente della richiesta.
Usa quella giusta per ciascun handler:
// 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');
Sono consentite più connessioni per (utente, provider), quindi lo stesso utente può avere affiancate “Linear personale” e “Linear di lavoro”.
Per ciascun provider di connessione, l’amministratore del server deve prima registrare un’app OAuth presso il servizio di terze parti.
  1. Vai alle impostazioni sviluppatore del provider (ad es. https://linear.app/settings/api/applications/new).
  2. Imposta la Redirect URI su \<SERVER_URL>/auth/apps/callback.
  3. Copia il Client ID e il Client Secret generati.
  4. Apri l’app installata in Twenty come amministratore del server → imposta i valori nelle corrispondenti serverVariables.
  5. I membri dello spazio di lavoro possono quindi aggiungere connessioni dalla sezione Connessioni dell’app.