# AI Agents in Twenty CRM: Complete Technical Architecture and Implementation Guide

> Explore the technical architecture of AI agents in Twenty CRM. Learn how administrators configure LLM assistants managed through NestJS and GraphQL mutations for user interaction.

- Repository: [Twenty/twenty](https://github.com/twentyhq/twenty)
- Tags: architecture
- Published: 2026-03-27

---

**Twenty CRM treats AI agents as first-class metadata objects managed through NestJS services and GraphQL mutations, enabling workspace administrators to configure LLM assistants that users invoke via persistent chat threads.**

Twenty CRM (twentyhq/twenty) implements a comprehensive AI agent system that embeds intelligent assistants directly into the CRM workflow. Unlike simple API integrations, the platform stores **AI agents in Twenty CRM** as core metadata entities, providing full CRUD capabilities through a type-safe GraphQL API and React frontend components.

## Agent Metadata Architecture

### Database Schema and Entity Definition

At the foundation of Twenty's AI system lies the **AgentEntity** class, which defines the database schema for agent storage. Located in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts), this entity extends `SyncableEntity` to ensure workspace-wide consistency across tenant migrations.

The entity captures essential LLM configuration parameters:

- `name` and `label` for identification
- `prompt` for system instructions
- `modelId` specifying the provider (e.g., "openai/gpt-4.1")
- `responseFormat` defining output structure
- Optional `icon` and `description` for UI representation

By extending `SyncableEntity`, the agent definition participates in Twenty's workspace migration system, allowing version-controlled deployment across different environments.

## Backend Services and GraphQL API

### AgentService Implementation

All agent lifecycle operations flow through **AgentService** in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.service.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.service.ts). This service implements standardized metadata patterns including `findManyAgents`, `findOneAgentById`, `createOneAgent`, `updateOneAgent`, and `deleteOneAgent`.

The `createOneAgent` method demonstrates the platform's migration-aware approach:

```typescript
// packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.service.ts
async createOneAgent(
  input: CreateAgentInput & { isCustom: boolean },
  workspaceId: string,
): Promise<FlatAgentWithRoleId> {
  // 1️⃣ Resolve application & role
  // 2️⃣ Convert DTO → flat‑agent representation
  const { flatAgentToCreate, flatRoleTargetToCreate } =
    fromCreateAgentInputToFlatAgent({ ... });

  // 3️⃣ Run workspace‑migration validation & apply
  const result = await this.workspaceMigrationValidateBuildAndRunService
    .validateBuildAndRunWorkspaceMigration({ ... });

  // 4️⃣ Retrieve the newly created flat‑agent from cache
  const createdAgent = findFlatEntityByIdInFlatEntityMapsOrThrow({ ... });

  return { ...createdAgent, roleId: flatRoleTargetToCreate?.roleId ?? null };
}

```

This implementation validates input against workspace migrations before persisting, ensuring schema consistency across the multi-tenant architecture.

### GraphQL Resolver Interface

The **AgentResolver** in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts) exposes these operations through the GraphQL layer. It forwards Data Transfer Objects (DTOs) directly to AgentService, providing mutations like `createOneAgent` and queries like `agents` without complex business logic duplication.

## Chat Thread Execution Model

When users interact with an agent, Twenty creates a structured conversation context through three hierarchical entities: **AgentChatThreadEntity**, **AgentTurnEntity**, and **AgentMessageEntity**.

### Thread and Message Management

The **AgentChatService** in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts) orchestrates conversation flow. It handles thread creation, message persistence, and LLM invocation coordination.

The `addMessage` method manages the complex state transitions:

```typescript
// packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts
async addMessage({
  threadId,
  uiMessage,
  agentId,
  turnId,
}: {
  threadId: string;
  uiMessage: Omit<ExtendedUIMessage, 'id'>;
  agentId?: string;
  turnId?: string;
}) {
  // Auto‑create a turn if none supplied
  if (!turnId) {
    const turn = this.turnRepository.create({ threadId, agentId: agentId ?? null });
    const savedTurn = await this.turnRepository.save(turn);
    turnId = savedTurn.id;
  }

  const message = this.messageRepository.create({
    threadId,
    turnId,
    role: uiMessage.role as AgentMessageRole,
    agentId: agentId ?? null,
  });
  const savedMessage = await this.messageRepository.save(message);

  // Persist any UI parts (files, tool calls)
  if (uiMessage.parts?.length) {
    const dbParts = mapUIMessagePartsToDBParts(uiMessage.parts, savedMessage.id);
    await this.messagePartRepository.save(dbParts);
  }

  return savedMessage;
}

```

This architecture supports rich message content through **AgentMessagePartEntity**, enabling file attachments and tool call sequences within conversations.

### Title Generation Service

Twenty automatically generates human-readable thread titles via **AgentTitleGenerationService** in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-title-generation.service.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-title-generation.service.ts), invoked when the first user message arrives. This eliminates manual thread naming while maintaining conversation organization.

## Frontend Integration

### Settings Management Interface

Administrators configure agents through the **Settings → AI → Agents** interface. The **SettingsAIAgentsTable** component in [`packages/twenty-front/src/pages/settings/ai/components/SettingsAIAgentsTable.tsx`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-front/src/pages/settings/ai/components/SettingsAIAgentsTable.tsx) retrieves agent lists using the generated GraphQL query `FindManyAgentsDocument`:

```tsx
import { useQuery } from '@apollo/client';
import { FindManyAgentsDocument } from '@/generated-metadata/graphql';

export const SettingsAIAgentsTable = () => {
  const { data, loading, error } = useQuery(FindManyAgentsDocument);

  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;

  return (
    <Table>
      {data?.agents?.map(agent => (
        <tr key={agent.id}>
          <td>{agent.label}</td>
          <td>{agent.modelId}</td>
          <td>{agent.description}</td>
        </tr>
      ))}
    </Table>
  );
};

```

Agent creation and editing utilize **SettingsAIAgentForm** in [`packages/twenty-front/src/pages/settings/ai/forms/components/SettingsAIAgentForm.tsx`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-front/src/pages/settings/ai/forms/components/SettingsAIAgentForm.tsx), which maps form fields to the `CreateAgentInput` GraphQL type.

### Conversation Initialization Hooks

End-users trigger agents through the **useCreateAgentChatThread** hook located in [`packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts):

```tsx
// packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts
export const useCreateAgentChatThread = () => {
  const [createThread] = useMutation(CreateOneAgentChatThreadDocument);
  return async (agentId: string) => {
    const { data } = await createThread({ variables: { agentId } });
    return data?.createOneAgentChatThread?.id;
  };
};

```

This hook invokes the `CreateOneAgentChatThread` mutation, which instantiates an `AgentChatThreadEntity` linked to the specified `agentId`.

## Practical Implementation Examples

### Creating an Agent via GraphQL

Developers can programmatically create agents using the type-safe GraphQL API:

```graphql
mutation CreateOneAgent($input: CreateAgentInput!) {
  createOneAgent(input: $input) {
    id
    name
    label
    prompt
    modelId
    responseFormat {
      type
    }
    isCustom
  }
}

```

Using the generated TypeScript client:

```typescript
import { createOneAgent } from '@/generated-metadata/graphql';

await createOneAgent({
  variables: {
    input: {
      name: 'sales-assistant',
      label: 'Sales Assistant',
      prompt: 'You are a helpful sales assistant. Answer concisely.',
      modelId: 'openai/gpt-4.1',
      responseFormat: { type: 'text' },
      isCustom: true,
    },
  },
});

```

### Starting a Conversation

The following React component demonstrates initiating a chat thread and sending the first message:

```tsx
import { useCreateAgentChatThread } from '@/modules/ai/hooks/useCreateAgentChatThread';
import { useMutation } from '@apollo/client';
import { AddAgentMessageDocument } from '@/generated-metadata/graphql';

export const ChatBox = ({ agentId }: { agentId: string }) => {
  const createThread = useCreateAgentChatThread();
  const [addMessage] = useMutation(AddAgentMessageDocument);

  const startConversation = async (text: string) => {
    const threadId = await createThread(agentId);
    await addMessage({
      variables: {
        threadId,
        uiMessage: { role: 'user', content: text },
      },
    });
  };

  // UI omitted
};

```

## Summary

- **AI agents in Twenty CRM** persist as metadata entities via `AgentEntity`, enabling version-controlled configuration across workspaces.
- **AgentService** and **AgentResolver** provide the backend CRUD operations and GraphQL API surface for agent management.
- **AgentChatService** manages conversational state through threads, turns, and messages, supporting complex interactions with file attachments and tool calls.
- The **React frontend** consumes these APIs through generated GraphQL documents and custom hooks like `useCreateAgentChatThread`.
- All operations integrate with Twenty's workspace migration system, ensuring schema consistency in multi-tenant deployments.

## Frequently Asked Questions

### How does Twenty CRM store AI agent configurations?

Twenty CRM stores AI agents in the `agent` database table defined by `AgentEntity` in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts). The entity captures the agent's name, prompt, model ID, response format, and optional metadata like icons and descriptions. Because it extends `SyncableEntity`, these configurations propagate through workspace migrations to maintain consistency across tenant environments.

### What GraphQL mutations are available for managing AI agents?

The platform exposes `createOneAgent`, `updateOneAgent`, and `deleteOneAgent` mutations through the `AgentResolver`, along with queries like `agents` and `searchAgents`. These endpoints accept strongly-typed inputs such as `CreateAgentInput` and forward requests to `AgentService`, which handles validation and workspace migration integration before persisting changes.

### How does the chat thread system handle message persistence?

When users send messages, `AgentChatService` in [`packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts) creates `AgentTurnEntity` and `AgentMessageEntity` records. The service auto-generates turns if not provided, persists message content and optional parts (files or tool calls) via `AgentMessagePartEntity`, and coordinates with the AI SDK to stream LLM responses back to the client.

### Can developers extend the AI agent system with custom models?

Yes, the `modelId` field in `AgentEntity` accepts arbitrary provider strings (e.g., "openai/gpt-4.1"), and the `modelConfiguration` JSON field allows additional provider-specific parameters. The execution layer uses the AI SDK (such as `@ai-sdk/openai`) to invoke the specified model, meaning any model supported by the underlying SDK integration can be configured through the standard agent creation interface.