Saltar para o conteúdo principal
Logic functions are server-side TypeScript functions that run on the Twenty platform. They can be triggered by HTTP requests, cron schedules, or database events — and can also be exposed as tools for AI agents.
Cada arquivo de função usa defineLogicFunction() para exportar uma configuração com um manipulador e gatilhos opcionais.
src/logic-functions/createPostCard.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define';
import { CoreApiClient, type Person } from 'twenty-client-sdk/core';

const handler = async (params: RoutePayload) => {
  const client = new CoreApiClient();
  const name = 'name' in params.queryStringParameters
    ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
    : 'Hello world';

  const result = await client.mutation({
    createPostCard: {
      __args: { data: { name } },
      id: true,
      name: true,
    },
  });
  return result;
};

export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'create-new-post-card',
  timeoutSeconds: 2,
  handler,
  httpRouteTriggerSettings: {
    path: '/post-card/create',
    httpMethod: 'GET',
    isAuthRequired: true,
  },
  /*databaseEventTriggerSettings: {
    eventName: 'people.created',
  },*/
  /*cronTriggerSettings: {
    pattern: '0 0 1 1 *',
  },*/
});
Tipos de gatilho disponíveis:
  • httpRoute: Expõe sua função em um caminho e método HTTP no endpoint /s/:
por exemplo, path: '/post-card/create' é acessível em https://your-twenty-server.com/s/post-card/create
  • cron: Executa sua função em um agendamento usando uma expressão CRON.
  • databaseEvent: Executa em eventos do ciclo de vida de objetos do espaço de trabalho. Quando a operação do evento é updated, campos específicos a serem observados podem ser especificados no array updatedFields. Se deixar indefinido ou vazio, qualquer atualização acionará a função.
por exemplo, person.updated, *.created, company.*
Você também pode executar manualmente uma função usando a CLI:
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
Você pode acompanhar os logs com:
yarn twenty logs

Payload de gatilho de rota

Quando um gatilho de rota invoca sua função de lógica, ela recebe um objeto RoutePayload que segue o formato HTTP API v2 da AWS. Importe o tipo RoutePayload de twenty-sdk:
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';

const handler = async (event: RoutePayload) => {
  const { headers, queryStringParameters, pathParameters, body } = event;
  const { method, path } = event.requestContext.http;

  return { message: 'Success' };
};
O tipo RoutePayload tem a seguinte estrutura:
PropriedadeTipoDescriçãoExemplo
headersRecord\<string, string | undefined>Cabeçalhos HTTP (apenas aqueles listados em forwardedRequestHeaders)veja a seção abaixo
queryStringParametersRecord\<string, string | undefined>Parâmetros de query string (valores múltiplos unidos por vírgulas)/users?ids=1&ids=2&ids=3&name=Alice -> { ids: '1,2,3', name: 'Alice' }
pathParametersRecord\<string, string | undefined>Parâmetros de caminho extraídos do padrão de rota/users/:id, /users/123 -> { id: '123' }
bodyobject | nullCorpo da requisição analisado (JSON){ id: 1 } -> { id: 1 }
isBase64EncodedbooleanSe o corpo está codificado em base64
requestContext.http.methodstringMétodo HTTP (GET, POST, PUT, PATCH, DELETE)
requestContext.http.pathstringCaminho bruto da requisição

forwardedRequestHeaders

Por padrão, os cabeçalhos HTTP das requisições recebidas não são repassados para sua função de lógica por motivos de segurança. Para acessar cabeçalhos específicos, liste-os explicitamente no array forwardedRequestHeaders:
export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'webhook-handler',
  handler,
  httpRouteTriggerSettings: {
    path: '/webhook',
    httpMethod: 'POST',
    isAuthRequired: false,
    forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
  },
});
No seu manipulador, acesse os cabeçalhos encaminhados assim:
const handler = async (event: RoutePayload) => {
  const signature = event.headers['x-webhook-signature'];
  const contentType = event.headers['content-type'];

  // Validate webhook signature...
  return { received: true };
};
Os nomes dos cabeçalhos são normalizados para minúsculas. Acesse-os usando chaves em minúsculas (por exemplo, event.headers['content-type']).

Expor uma função como ferramenta

Funções lógicas podem ser expostas como ferramentas para agentes de IA e fluxos de trabalho. Quando marcada como ferramenta, uma função fica detectável pelos recursos de IA do Twenty e pode ser usada em automações de fluxos de trabalho.Para marcar uma função de lógica como ferramenta, defina isTool: true:
src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async (params: { companyName: string; domain?: string }) => {
  const client = new CoreApiClient();

  const result = await client.mutation({
    createTask: {
      __args: {
        data: {
          title: `Enrich data for ${params.companyName}`,
          body: `Domain: ${params.domain ?? 'unknown'}`,
        },
      },
      id: true,
    },
  });

  return { taskId: result.createTask.id };
};

export default defineLogicFunction({
  universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  name: 'enrich-company',
  description: 'Enrich a company record with external data',
  timeoutSeconds: 10,
  handler,
  isTool: true,
});
Pontos-chave:
  • Você pode combinar isTool com gatilhos — uma função pode ser ao mesmo tempo uma ferramenta (chamável por agentes de IA) e acionada por eventos.
  • toolInputSchema (opcional): Um objeto JSON Schema que descreve os parâmetros que sua função aceita. O schema é calculado automaticamente a partir da análise estática do código-fonte, mas você pode defini-lo explicitamente:
export default defineLogicFunction({
  ...,
  toolInputSchema: {
    type: 'object',
    properties: {
      companyName: {
        type: 'string',
        description: 'The name of the company to enrich',
      },
      domain: {
        type: 'string',
        description: 'The company website domain (optional)',
      },
    },
    required: ['companyName'],
  },
});
Escreva uma boa description. Os agentes de IA dependem do campo description da função para decidir quando usar a ferramenta. Seja específico sobre o que a ferramenta faz e quando ela deve ser chamada.
Uma função de pós-instalação é uma função lógica que é executada automaticamente assim que seu aplicativo terminar de ser instalado em um espaço de trabalho. O servidor a executa depois que os metadados do aplicativo forem sincronizados e o cliente do SDK for gerado, para que o espaço de trabalho esteja totalmente pronto para uso e o novo esquema esteja disponível. Casos de uso típicos incluem popular dados padrão, criar registros iniciais, configurar as definições do espaço de trabalho ou provisionar recursos em serviços de terceiros.
src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';

const handler = async (payload: InstallPayload): Promise<void> => {
  console.log('Post install logic function executed successfully!', payload.previousVersion);
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Runs after installation to set up the application.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  shouldRunSynchronously: false,
  handler,
});
Você também pode executar manualmente a função de pós-instalação a qualquer momento usando a CLI:
yarn twenty exec --postInstall
Pontos-chave:
  • As funções de pós-instalação usam definePostInstallLogicFunction() — uma variante especializada que omite as configurações de gatilho (cronTriggerSettings, databaseEventTriggerSettings, httpRouteTriggerSettings, isTool).
  • O manipulador recebe um InstallPayload com { previousVersion?: string; newVersion: string }newVersion é a versão que está sendo instalada, e previousVersion é a versão que foi instalada anteriormente (ou undefined em uma instalação nova). Use esses valores para distinguir instalações novas de atualizações e para executar lógica de migração específica da versão.
  • Quando o hook é executado: apenas em instalações novas, por padrão. Passe shouldRunOnVersionUpgrade: true se você também quiser que ele seja executado quando o app for atualizado a partir de uma versão anterior. Quando omitida, a flag tem valor padrão false e as atualizações ignoram o hook.
  • Modelo de execução — assíncrono por padrão, síncrono opcional: a flag shouldRunSynchronously controla como a pós-instalação é executada.
    • shouldRunSynchronously: false (padrão) — o hook é enfileirado na fila de mensagens com retryLimit: 3 e é executado de forma assíncrona em um worker. A resposta da instalação retorna assim que o job é enfileirado, então um manipulador lento ou com falha não bloqueia quem chamou. O worker tentará novamente até três vezes. Use isto para jobs de longa duração — popular grandes conjuntos de dados, chamar APIs de terceiros lentas, provisionar recursos externos, qualquer coisa que possa exceder uma janela razoável de resposta HTTP.
    • shouldRunSynchronously: true — o hook é executado inline durante o fluxo de instalação (mesmo executor da pré-instalação). A requisição de instalação bloqueia até o manipulador terminar e, se ele lançar uma exceção, quem chamou a instalação recebe um POST_INSTALL_ERROR. Sem novas tentativas automáticas. Use isto para trabalhos rápidos que precisam ser concluídos antes da resposta — por exemplo, emitir um erro de validação para o usuário ou fazer uma configuração rápida da qual o cliente dependerá imediatamente após a chamada de instalação retornar. Tenha em mente que a migração de metadados já foi aplicada quando a pós-instalação é executada, então uma falha no modo síncrono não reverte as alterações de esquema — ela apenas expõe o erro.
  • Garanta que seu manipulador seja idempotente. No modo assíncrono, a fila pode tentar novamente até três vezes; em qualquer modo, o hook pode ser executado novamente em atualizações quando shouldRunOnVersionUpgrade: true.
  • As variáveis de ambiente APPLICATION_ID, APP_ACCESS_TOKEN e API_URL estão disponíveis dentro do manipulador (assim como em qualquer outra função de lógica), então você pode chamar a API da Twenty com um token de acesso de aplicativo com escopo para o seu app.
  • É permitida apenas uma função de pós-instalação por app. A geração do manifesto apresentará erro se mais de uma for detectada.
  • O universalIdentifier, shouldRunOnVersionUpgrade e shouldRunSynchronously da função são anexados automaticamente ao manifesto do aplicativo no campo postInstallLogicFunction durante o build — você não precisa referenciá-los em defineApplication().
  • O tempo limite padrão é definido como 300 segundos (5 minutos) para permitir tarefas de configuração mais longas, como o pré-carregamento de dados.
  • Não executado no modo de desenvolvimento: quando um app é registrado localmente (via yarn twenty dev), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de shouldRunSynchronously. Use yarn twenty exec --postInstall para acioná-lo manualmente em um workspace em execução.
Uma função de pré-instalação é uma função de lógica que é executada automaticamente durante a instalação, antes que a migração de metadados do workspace seja aplicada. Ela compartilha o mesmo formato de payload que a pós-instalação (InstallPayload), mas está posicionada mais cedo no fluxo de instalação para poder preparar o estado do qual a próxima migração depende — usos típicos incluem fazer backup de dados, validar a compatibilidade com o novo esquema ou arquivar registros que estão prestes a ser reestruturados ou removidos.
src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';

const handler = async (payload: InstallPayload): Promise<void> => {
  console.log('Pre install logic function executed successfully!', payload.previousVersion);
};

export default definePreInstallLogicFunction({
  universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
  name: 'pre-install',
  description: 'Runs before installation to prepare the application.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: true,
  handler,
});
Você também pode executar manualmente a função de pré-instalação a qualquer momento usando a CLI:
yarn twenty exec --preInstall
Pontos-chave:
  • Funções de pré-instalação usam definePreInstallLogicFunction() — a mesma configuração especializada da pós-instalação, apenas anexada a um ponto diferente do ciclo de vida.
  • Os manipuladores de pré e pós-instalação recebem o mesmo tipo InstallPayload: { previousVersion?: string; newVersion: string }. Importe-o uma vez e reutilize-o para ambos os hooks.
  • Quando o hook é executado: posicionado imediatamente antes da migração de metadados do workspace (synchronizeFromManifest). Antes de executar, o servidor realiza uma “sincronização simplificada” puramente aditiva que registra a função de pré-instalação da nova versão nos metadados do workspace — nada mais é alterado — e então a executa. Como essa sincronização é apenas aditiva, os objetos, campos e dados da versão anterior ainda estão intactos quando seu manipulador é executado: você pode ler e fazer backup com segurança do estado pré-migração.
  • Modelo de execução: a pré-instalação é executada de forma síncrona e bloqueia a instalação. Se o manipulador lançar uma exceção, a instalação é abortada antes que quaisquer alterações de esquema sejam aplicadas — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada.
  • Assim como na pós-instalação, é permitida apenas uma função de pré-instalação por app. Ela é anexada ao manifesto do aplicativo sob preInstallLogicFunction automaticamente durante o build.
  • Não é executada no modo de desenvolvimento: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com yarn twenty dev. Use yarn twenty exec --preInstall para acioná-lo manualmente.
Ambos os hooks fazem parte do mesmo fluxo de instalação e recebem o mesmo InstallPayload. A diferença é quando eles são executados em relação à migração de metadados do workspace, e isso muda quais dados eles podem manipular com segurança.
┌─────────────────────────────────────────────────────────────┐
│ install flow                                                │
│                                                             │
│   upload package → [pre-install] → metadata migration →     │
│   generate SDK → [post-install]                             │
│                                                             │
│                  old schema visible    new schema visible   │
└─────────────────────────────────────────────────────────────┘
A pré-instalação é sempre síncrona (ela bloqueia a instalação e pode abortá-la). A pós-instalação é assíncrona por padrão — enfileirada em um worker com novas tentativas automáticas — mas pode optar por execução síncrona com shouldRunSynchronously: true. Veja o acordeão definePostInstallLogicFunction acima para saber quando usar cada modo.Use post-install para qualquer coisa que precise que o novo esquema exista. Este é o caso mais comum:
  • Popular dados padrão (criando registros iniciais, visualizações padrão, conteúdo de demonstração) em objetos e campos recém-adicionados.
  • Registrar webhooks com serviços de terceiros agora que o app tem suas credenciais.
  • Chamar sua própria API para finalizar a configuração que depende dos metadados sincronizados.
  • Lógica idempotente de “garantir que isso exista” que deve reconciliar o estado em cada atualização — combine com shouldRunOnVersionUpgrade: true.
Exemplo — popular um registro PostCard padrão após a instalação:
src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { createClient } from './generated/client';

const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
  if (previousVersion) return; // fresh installs only

  const client = createClient();
  await client.postCard.create({
    data: { title: 'Welcome to Postcard', content: 'Your first card!' },
  });
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Seeds a welcome post card after install.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  handler,
});
Use pre-install quando uma migração, de outra forma, destruiria ou corromperia dados existentes. Como a pré-instalação roda contra o esquema anterior e sua falha reverte a atualização, é o lugar certo para qualquer coisa arriscada:
  • Fazer backup de dados que estão prestes a ser removidos ou reestruturados — por exemplo, você está removendo um campo na v2 e precisa copiar seus valores para outro campo ou exportá-los para um armazenamento antes que a migração seja executada.
  • Arquivar registros que uma nova restrição invalidaria — por exemplo, um campo está se tornando NOT NULL e você precisa excluir ou corrigir linhas com valores nulos primeiro.
  • Validar a compatibilidade e recusar a atualização se os dados atuais não puderem ser migrados de forma limpa — lance uma exceção no manipulador e a instalação é abortada sem alterações aplicadas. Isto é mais seguro do que descobrir a incompatibilidade no meio da migração.
  • Renomear ou reatribuir chaves de dados antes de uma alteração de esquema que perderia a associação.
Exemplo — arquivar registros antes de uma migração destrutiva:
src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { createClient } from './generated/client';

const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
  // Only the 1.x → 2.x upgrade drops the legacy `notes` field.
  if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
    return;
  }

  const client = createClient();
  const legacyRecords = await client.postCard.findMany({
    where: { notes: { isNotNull: true } },
  });

  if (legacyRecords.length === 0) return;

  // Copy legacy `notes` into the new `description` field before the migration
  // drops the `notes` column. If this fails, the upgrade is aborted and the
  // workspace stays on v1 with all data intact.
  await Promise.all(
    legacyRecords.map((record) =>
      client.postCard.update({
        where: { id: record.id },
        data: { description: record.notes },
      }),
    ),
  );
};

export default definePreInstallLogicFunction({
  universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
  name: 'pre-install',
  description: 'Backs up legacy notes into description before the v2 migration.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: true,
  handler,
});
Regra geral:
You want to…Usar
Popular dados padrão, configurar o workspace, registrar recursos externospost-install
Executar processos longos de popular dados ou chamadas a terceiros que não devem bloquear a resposta da instalaçãopost-install (padrão — shouldRunSynchronously: false, com novas tentativas do worker)
Executar uma configuração rápida da qual o chamador dependerá imediatamente após o retorno da chamada de instalaçãopost-install com shouldRunSynchronously: true
Ler ou fazer backup de dados que a próxima migração perderiapre-install
Rejeitar uma atualização que corromperia dados existentespre-install (lançar uma exceção no manipulador)
Executar reconciliação em cada atualizaçãopost-install com shouldRunOnVersionUpgrade: true
Fazer uma configuração única apenas na primeira instalaçãopost-install com shouldRunOnVersionUpgrade: false (padrão)
Em caso de dúvida, use post-install como padrão. Recurra à pré-instalação somente quando a própria migração for destrutiva e você precisar interceptar o estado anterior antes que ele desapareça.

Clientes de API tipados (twenty-client-sdk)

O pacote twenty-client-sdk fornece dois clientes GraphQL tipados para interagir com a API do Twenty a partir das suas funções de lógica e componentes de front-end.
ClienteImportarEndpointGerado?
CoreApiClienttwenty-client-sdk/core/graphql — dados do espaço de trabalho (registros, objetos)Sim, em tempo de dev/build
MetadataApiClienttwenty-client-sdk/metadata/metadata — configuração do espaço de trabalho, upload de arquivosNão, vem pré-compilado
CoreApiClient é o cliente principal para consultar e mutar dados do espaço de trabalho. Ele é gerado a partir do schema do seu espaço de trabalho durante yarn twenty dev ou yarn twenty build, então é totalmente tipado para corresponder aos seus objetos e campos.
import { CoreApiClient } from 'twenty-client-sdk/core';

const client = new CoreApiClient();

// Query records
const { companies } = await client.query({
  companies: {
    edges: {
      node: {
        id: true,
        name: true,
        domainName: {
          primaryLinkLabel: true,
          primaryLinkUrl: true,
        },
      },
    },
  },
});

// Create a record
const { createCompany } = await client.mutation({
  createCompany: {
    __args: {
      data: {
        name: 'Acme Corp',
      },
    },
    id: true,
    name: true,
  },
});
O cliente usa uma sintaxe de selection-set: passe true para incluir um campo, use __args para argumentos e aninhe objetos para relações. Você tem preenchimento automático e verificação de tipos completos com base no schema do seu espaço de trabalho.
CoreApiClient é gerado em tempo de dev/build. Se você usá-lo sem executar primeiro yarn twenty dev ou yarn twenty build, ele lançará um erro. A geração ocorre automaticamente — a CLI analisa o schema GraphQL do seu espaço de trabalho e gera um cliente tipado usando @genql/cli.

Usando CoreSchema para anotações de tipo

CoreSchema fornece tipos TypeScript que correspondem aos objetos do seu espaço de trabalho — útil para tipar o estado de componentes ou parâmetros de função:
import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core';
import { useState } from 'react';

const [company, setCompany] = useState<
  Pick<CoreSchema.Company, 'id' | 'name'> | undefined
>(undefined);

const client = new CoreApiClient();
const result = await client.query({
  company: {
    __args: { filter: { position: { eq: 1 } } },
    id: true,
    name: true,
  },
});
setCompany(result.company);
MetadataApiClient é fornecido pré-compilado com o SDK (não é necessário gerar). Ele consulta o endpoint /metadata para configuração do espaço de trabalho, aplicativos e upload de arquivos.
import { MetadataApiClient } from 'twenty-client-sdk/metadata';

const metadataClient = new MetadataApiClient();

// List first 10 objects in the workspace
const { objects } = await metadataClient.query({
  objects: {
    edges: {
      node: {
        id: true,
        nameSingular: true,
        namePlural: true,
        labelSingular: true,
        isCustom: true,
      },
    },
    __args: {
      filter: {},
      paging: { first: 10 },
    },
  },
});

Carregamento de arquivos

MetadataApiClient inclui um método uploadFile para anexar arquivos a campos do tipo arquivo:
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import * as fs from 'fs';

const metadataClient = new MetadataApiClient();

const fileBuffer = fs.readFileSync('./invoice.pdf');

const uploadedFile = await metadataClient.uploadFile(
  fileBuffer,                                         // file contents as a Buffer
  'invoice.pdf',                                      // filename
  'application/pdf',                                  // MIME type
  '58a0a314-d7ea-4865-9850-7fb84e72f30b',            // field universalIdentifier
);

console.log(uploadedFile);
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
ParâmetroTipoDescrição
fileBufferBufferO conteúdo bruto do arquivo
filenamestringO nome do arquivo (usado para armazenamento e exibição)
contentTypestringTipo MIME (padrão para application/octet-stream se omitido)
fieldMetadataUniversalIdentifierstringO universalIdentifier do campo do tipo de arquivo no seu objeto
Pontos-chave:
  • Usa o universalIdentifier do campo (não o ID específico do espaço de trabalho), de modo que seu código de upload funcione em qualquer espaço de trabalho onde seu app esteja instalado.
  • A url retornada é uma URL assinada que você pode usar para acessar o arquivo enviado.
Quando seu código é executado no Twenty (funções de lógica ou componentes de front-end), a plataforma injeta credenciais como variáveis de ambiente:
  • TWENTY_API_URL — URL base da API do Twenty
  • TWENTY_APP_ACCESS_TOKEN — Chave de curta duração com escopo para o papel de função padrão do seu aplicativo
Você não precisa passá-las para os clientes — eles leem de process.env automaticamente. As permissões da chave de API são determinadas pelo papel referenciado em defaultRoleUniversalIdentifier no seu application-config.ts.