Saltar para o conteúdo principal
Conexões são credenciais que um usuário mantém para um serviço externo (Linear, GitHub, Slack, …). Seu app declara como essas credenciais são obtidas — um provedor de conexão — e as consome em tempo de execução para fazer chamadas autenticadas à API de terceiros. Atualmente, apenas o OAuth 2.0 tem suporte. Tipos de credenciais futuros (tokens de acesso pessoal, chaves de API, autenticação básica) serão conectados à mesma interface — apps que já usam defineConnectionProvider({ type: 'oauth', ... }) não precisarão migrar.
Um provedor de conexão descreve o handshake OAuth de que seu app precisa. O usuário clica em “Adicionar conexão” nas configurações do seu app, conclui a tela de consentimento do provedor e uma linha ConnectedAccount é criada no seu workspace.Uma configuração funcional precisa de dois arquivos — o provedor de conexão e uma declaração correspondente de serverVariables em defineApplication que contém as credenciais do cliente 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,
    },
  },
});
Pontos-chave:
  • name é a string de identificador exclusivo usada em listConnections({ providerName }) (kebab-case, deve corresponder a ^[a-z][a-z0-9-]*$).
  • displayName aparece na aba de configurações do app e na lista de ferramentas de IA.
  • clientIdVariable / clientSecretVariable são nomes, não valores — devem corresponder às chaves declaradas em defineApplication.serverVariables. Os client_id e client_secret reais são inseridos pelo administrador do servidor por meio da interface de registro do app e nunca são versionados no seu repositório.
  • Use serverVariables (não applicationVariables) — as credenciais OAuth são do servidor como um todo e há um app OAuth por servidor do Twenty.
  • Até que ambos os serverVariables sejam preenchidos, a aba de configurações do app mostra uma dica “precisa de administrador do servidor” e o botão “Adicionar conexão” fica desativado.
  • type: 'oauth' é o único valor compatível atualmente. O discriminador é compatível com versões futuras: tipos futuros ('pat', 'api-key', …) adicionarão novos blocos de subconfiguração ao lado de oauth.
O URL de callback do OAuth que seu provedor precisa adicionar à lista de permissões é:
https://<your-twenty-server>/auth/apps/callback
Dentro de um handler de função de lógica, listConnections({ providerName }) retorna as linhas ConnectedAccount deste app para o provedor fornecido, com tokens de acesso atualizados.
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 };
};
Cada conexão tem:
CampoDescrição
idID de linha exclusivo; passe para getConnection(id) para buscar novamente um único registro
visibility'user' (privada para um membro do workspace) ou 'workspace' (compartilhada com todos os membros)
scopesPermissões OAuth concedidas pelo provedor de origem (distintas de visibility — não têm relação)
userWorkspaceIdO id de userWorkspace do proprietário — útil para selecionar “a conexão do usuário da requisição” em gatilhos de rota HTTP
accessTokenToken de acesso OAuth recente (atualizado automaticamente se estiver expirado)
name / handleO nome de exibição da conexão (derivado automaticamente no callback do OAuth, renomeável pelo usuário)
authFailedAtDefinido quando a atualização mais recente falhou; o usuário deve reconectar
Pontos-chave:
  • Passe { providerName } para filtrar por provedor; omita para obter todas as conexões que este app possui em todos os provedores.
  • O servidor atualiza transparentemente o token de acesso antes de retornar. Seu handler sempre vê um token utilizável (ou authFailedAt definido).
  • getConnection(id) é o equivalente de uma única linha.
Quando um usuário clica em “Adicionar conexão”, é solicitado que escolha uma visibilidade:
  • Apenas para mim — a credencial é privada para o usuário que a conectou. Qualquer função de lógica chamada em seu nome (gatilho de rota HTTP com isAuthRequired: true) a vê; gatilhos cron e eventos de banco de dados não.
  • Compartilhada no workspace — qualquer membro do workspace pode usar a credencial. Gatilhos de cron / banco de dados também a veem, pois não há um usuário da requisição.
Use a adequada para cada 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');
Várias conexões por (usuário, provedor) são permitidas, então o mesmo usuário pode manter “Linear pessoal” e “Linear de trabalho” lado a lado.
Para cada provedor de conexão, o administrador do servidor precisa primeiro registrar um app OAuth no serviço de terceiros.
  1. Acesse as configurações de desenvolvedor do provedor (por exemplo, https://linear.app/settings/api/applications/new).
  2. Defina a URI de redirecionamento como \<SERVER_URL>/auth/apps/callback.
  3. Copie o ID do cliente e o Segredo do cliente gerados.
  4. Abra o app instalado no Twenty como administrador do servidor → defina os valores nos serverVariables correspondentes.
  5. Os membros do workspace podem então adicionar conexões na seção Conexões de cada app.