Перейти к основному содержанию
Подключения — это учетные данные, которыми пользователь располагает для внешнего сервиса (Linear, GitHub, Slack, …). Ваше приложение определяет, как получают эти учетные данные — через провайдера подключения — и использует их во время выполнения для выполнения аутентифицированных вызовов к стороннему API. На данный момент поддерживается только OAuth 2.0. Будущие типы учетных данных (персональные токены доступа, ключи API, базовая аутентификация) будут подключаться к тому же интерфейсу — приложения, уже использующие defineConnectionProvider({ type: 'oauth', ... }) не потребуют миграции.
Провайдер подключения описывает процедуру OAuth-обмена, которая требуется вашему приложению. Пользователь нажимает “Добавить подключение” в настройках вашего приложения, подтверждает разрешения на экране согласия провайдера, и в его рабочем пространстве создается запись ConnectedAccount.Рабочей конфигурации нужны два файла — провайдер подключения и соответствующее объявление serverVariables в defineApplication, которое содержит учетные данные клиента 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,
    },
  },
});
Основные моменты:
  • name — это уникальная строка-идентификатор, используемая в listConnections({ providerName }) (kebab-case, должна соответствовать ^[a-z][a-z0-9-]*$).
  • displayName отображается на вкладке настроек приложения и в списке инструментов ИИ.
  • clientIdVariable / clientSecretVariable — это имена, а не значения — они должны совпадать с ключами, объявленными в defineApplication.serverVariables. Фактические client_id и client_secret вводятся администратором сервера через интерфейс регистрации приложения и никогда не коммитятся в ваш репозиторий.
  • Используйте serverVariables (не applicationVariables) — учетные данные OAuth являются общими для сервера, и на каждом сервере Twenty используется одно приложение OAuth.
  • Пока оба serverVariables не заполнены, на вкладке настроек приложения показывается подсказка “нужен администратор сервера”, а кнопка “Добавить подключение” отключена.
  • type: 'oauth' — единственное поддерживаемое сегодня значение. Дискриминатор совместим с будущими версиями: будущие типы ('pat', 'api-key', …) добавят новые блоки подконфигурации рядом с oauth.
URL обратного вызова OAuth, который вашему провайдеру нужно добавить в список разрешенных:
https://<your-twenty-server>/auth/apps/callback
Внутри обработчика логической функции listConnections({ providerName }) возвращает записи ConnectedAccount этого приложения для указанного провайдера с обновленными токенами доступа.
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 };
};
Каждое подключение имеет:
ПолеОписание
idУникальный идентификатор записи; передайте его в getConnection(id), чтобы повторно получить одну запись
visibility'user' (приватно для одного участника рабочего пространства) или 'workspace' (доступно всем участникам)
scopesРазрешения OAuth, предоставленные внешним провайдером (отличаются от visibility — это несвязанные вещи)
userWorkspaceIdИдентификатор userWorkspace владельца — полезно для выбора “подключения пользователя запроса” в триггерах HTTP-маршрутов
accessTokenАктуальный токен доступа OAuth (обновляется автоматически при истечении срока действия)
name / handleОтображаемое имя подключения (автоматически определяется при обратном вызове OAuth, может быть переименовано пользователем)
authFailedAtУстанавливается, если последняя попытка обновления не удалась; пользователю нужно переподключиться
Основные моменты:
  • Передайте { providerName }, чтобы отфильтровать по провайдеру; опустите, чтобы получить все подключения этого приложения у всех провайдеров.
  • Сервер прозрачно обновляет токен доступа перед возвратом. Ваш обработчик всегда получает рабочий токен (или установлено authFailedAt).
  • getConnection(id) — эквивалент для одной записи.
Когда пользователь нажимает “Добавить подключение”, ему предлагается выбрать видимость:
  • Только для меня — учетные данные приватны для подключившегося пользователя. Любая логическая функция, вызываемая от его имени (триггер HTTP-маршрута с isAuthRequired: true), видит их; триггеры cron и события базы данных — нет.
  • Общее для рабочего пространства — любой участник рабочего пространства может использовать эти учетные данные. Триггеры cron/базы данных также видят их, поскольку у них нет пользователя запроса.
Используйте подходящий вариант для каждого обработчика:
// 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');
Допускается несколько подключений на пару (пользователь, провайдер), поэтому один и тот же пользователь может иметь “Personal Linear” и “Work Linear” одновременно.
Для каждого провайдера подключения администратору сервера сначала нужно зарегистрировать у стороннего сервиса приложение OAuth.
  1. Перейдите в настройки разработчика провайдера (например, https://linear.app/settings/api/applications/new).
  2. Установите Redirect URI в значение \<SERVER_URL>/auth/apps/callback.
  3. Скопируйте сгенерированные Client ID и Client Secret.
  4. Откройте установленное приложение в Twenty под учетной записью администратора сервера → задайте значения в соответствующих serverVariables.
  5. Затем участники рабочего пространства смогут добавлять подключения в разделе Подключения конкретного приложения.