Passer au contenu principal
Les connexions sont des identifiants qu’un utilisateur détient pour un service externe (Linear, GitHub, Slack, …). Votre application déclare comment ces identifiants sont obtenus — un fournisseur de connexion — et les consomme à l’exécution pour effectuer des appels authentifiés vers l’API tierce. À ce jour, seul OAuth 2.0 est pris en charge. De futurs types d’identifiants (jetons d’accès personnels, clés d’API, authentification de base) s’intégreront à la même interface — les applications utilisant déjà defineConnectionProvider({ type: 'oauth', ... })` n’auront pas besoin de migrer.
Un fournisseur de connexion décrit le processus d’échange OAuth requis par votre application. L’utilisateur clique sur “Ajouter une connexion” dans les paramètres de votre application, complète l’écran de consentement du fournisseur, et une ligne ConnectedAccount est créée dans son espace de travail.Une configuration fonctionnelle nécessite deux fichiers — le fournisseur de connexion, et une déclaration serverVariables correspondante sur defineApplication qui contient les identifiants 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,
    },
  },
});
Points clés :
  • name est l’identifiant unique utilisé dans listConnections({ providerName }) (kebab-case, doit correspondre à ^[a-z][a-z0-9-]*$).
  • displayName s’affiche dans l’onglet des paramètres par application et dans la liste des outils d’IA.
  • clientIdVariable / clientSecretVariable sont des noms, pas des valeurs — ils doivent correspondre aux clés déclarées dans defineApplication.serverVariables. Les client_id et client_secret réels sont saisis par l’administrateur du serveur via l’interface d’enregistrement de l’application, et ne sont jamais validés dans votre dépôt.
  • Utilisez serverVariables (pas applicationVariables) — les identifiants OAuth sont au niveau du serveur et il n’y a qu’une application OAuth par serveur Twenty.
  • Tant que les deux serverVariables ne sont pas renseignées, l’onglet des paramètres par application affiche une indication “nécessite un administrateur du serveur” et le bouton “Ajouter une connexion” est désactivé.
  • type: 'oauth' est la seule valeur prise en charge à ce jour. Le discriminateur est compatible avec les évolutions : des types futurs ('pat', 'api-key', …) ajouteront de nouveaux blocs de sous-configuration à côté de oauth.
L’URL de rappel OAuth que votre fournisseur doit autoriser est :
https://<your-twenty-server>/auth/apps/callback
Dans un gestionnaire de fonction logique, listConnections({ providerName }) renvoie les lignes ConnectedAccount de cette application pour le fournisseur donné, avec des jetons d’accès actualisés.
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 };
};
Chaque connexion comporte :
ChampDescription
idID de ligne unique ; à passer à getConnection(id) pour en récupérer une seule
visibility'user' (réservée à un membre de l’espace de travail) ou 'workspace' (partagée avec tous les membres)
scopesAutorisations OAuth accordées par le fournisseur en amont (distinctes de visibility — sans rapport)
userWorkspaceIdL’ID userWorkspace du propriétaire — utile pour sélectionner “la connexion de l’utilisateur de la requête” dans les déclencheurs de routes HTTP
accessTokenJeton d’accès OAuth à jour (actualisé automatiquement s’il a expiré)
name / handleLe nom d’affichage de la connexion (déduit automatiquement lors du callback OAuth, renommable par l’utilisateur)
authFailedAtDéfini lorsque le rafraîchissement le plus récent a échoué ; l’utilisateur doit se reconnecter
Points clés :
  • Passez { providerName } pour filtrer par fournisseur ; omettez-le pour obtenir toutes les connexions que cette application possède sur l’ensemble des fournisseurs.
  • Le serveur actualise de manière transparente le jeton d’accès avant de répondre. Votre gestionnaire voit toujours un jeton utilisable (ou authFailedAt défini).
  • getConnection(id) est l’équivalent pour une seule ligne.
Lorsqu’un utilisateur clique sur “Ajouter une connexion”, il lui est demandé de choisir une visibilité :
  • Uniquement pour moi — les identifiants sont privés pour l’utilisateur qui se connecte. Toute fonction logique appelée en son nom (déclencheur de route HTTP avec isAuthRequired: true) y a accès ; les déclencheurs cron et les événements de base de données n’y ont pas accès.
  • Partagé au niveau de l’espace de travail — tout membre de l’espace de travail peut utiliser les identifiants. Les déclencheurs cron / base de données y ont également accès, puisqu’ils n’ont pas d’utilisateur de requête.
Utilisez le bon pour chaque gestionnaire :
// 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');
Plusieurs connexions par (utilisateur, fournisseur) sont autorisées, ainsi le même utilisateur peut avoir “Linear personnel” et “Linear professionnel” côte à côte.
Pour chaque fournisseur de connexion, l’administrateur du serveur doit d’abord enregistrer une application OAuth auprès du service tiers.
  1. Accédez aux paramètres développeur du fournisseur (par ex. https://linear.app/settings/api/applications/new).
  2. Définissez l’URI de redirection sur \<SERVER_URL>/auth/apps/callback.
  3. Copiez l’ID client et le Secret client générés.
  4. Ouvrez l’application installée dans Twenty en tant qu’administrateur du serveur → définissez les valeurs sur les serverVariables correspondantes.
  5. Les membres de l’espace de travail peuvent ensuite ajouter des connexions depuis la section Connexions par application.