# Integrations, Extension Points & Operational Surfaces

> External integration surfaces: Zapier connector, REST/GraphQL client SDK (twenty-client-sdk), email templates (twenty-emails), connected-account sync (messaging, calendar), AI/code-interpreter modules, and the Docker Compose dev/prod setup. How to extend Twenty with new workspace modules, custom objects, and external connectors.

- Repository: twentyhq/twenty
- GitHub: https://github.com/twentyhq/twenty
- Human wiki: https://grok-wiki.com/public/wiki/twentyhq-twenty-7ed82e5a21f6
- Complete Markdown: https://grok-wiki.com/public/wiki/twentyhq-twenty-7ed82e5a21f6/llms-full.txt

## Source Files

- `packages/twenty-zapier`
- `packages/twenty-client-sdk`
- `packages/twenty-emails/src`
- `packages/twenty-server/src/modules/connected-account`
- `packages/twenty-server/src/engine/core-modules/ai`
- `packages/twenty-docker`
- `packages/twenty-e2e-testing`
- `packages/twenty-utils/setup-dev-env.sh`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [packages/twenty-zapier/src/index.ts](packages/twenty-zapier/src/index.ts)
- [packages/twenty-zapier/src/authentication.ts](packages/twenty-zapier/src/authentication.ts)
- [packages/twenty-zapier/src/triggers/trigger_record.ts](packages/twenty-zapier/src/triggers/trigger_record.ts)
- [packages/twenty-client-sdk/src/generate/twenty-client-template.ts](packages/twenty-client-sdk/src/generate/twenty-client-template.ts)
- [packages/twenty-server/src/engine/metadata-modules/connected-account/dtos/connected-account.dto.ts](packages/twenty-server/src/engine/metadata-modules/connected-account/dtos/connected-account.dto.ts)
- [packages/twenty-server/src/engine/core-modules/sdk-client/jobs/generate-sdk-client.job.ts](packages/twenty-server/src/engine/core-modules/sdk-client/jobs/generate-sdk-client.job.ts)
- [packages/twenty-server/src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job.ts](packages/twenty-server/src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job.ts)
- [packages/twenty-server/src/modules/calendar/calendar-event-cleaner/jobs/delete-connected-account-associated-calendar-data.job.ts](packages/twenty-server/src/modules/calendar/calendar-event-cleaner/jobs/delete-connected-account-associated-calendar-data.job.ts)
- [packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts](packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts)
- [packages/twenty-docker/docker-compose.yml](packages/twenty-docker/docker-compose.yml)
- [packages/twenty-docker/docker-compose.dev.yml](packages/twenty-docker/docker-compose.dev.yml)
</details>

# Integrations, Extension Points & Operational Surfaces

Twenty is structured as an Nx monorepo in which each major external integration surface lives in its own package or server module. This page documents how Twenty exposes itself to the outside world (Zapier, GraphQL/REST clients, email), how it pulls external data in (connected accounts for Gmail, Microsoft, and calendar), how background workers process integration events, and how the full system is operated via Docker Compose for both development and production.

Understanding these surfaces is essential for anyone extending Twenty with new workspace modules, custom objects, or external connectors, since each integration pattern follows distinct lifecycle, authentication, and worker-queue conventions.

---

## Zapier Connector (`packages/twenty-zapier`)

Twenty ships a first-party Zapier integration built with `zapier-platform-core`. It is published as its own package and connects to the Twenty GraphQL API using an API key.

### Authentication

The connector uses Zapier's `custom` authentication type. Users provide:

| Field | Required | Description |
|---|---|---|
| `apiKey` | Yes | API key created in Twenty workspace settings |
| `apiUrl` | No | Override URL for self-hosted instances |

Authentication is tested by executing a `currentWorkspace { id displayName }` metadata query. The connection label shown in Zapier is derived from `currentWorkspace.displayName`.

Sources: [packages/twenty-zapier/src/authentication.ts:5-39](packages/twenty-zapier/src/authentication.ts)

### Triggers and Actions

The connector registers three triggers and one action:

| Component | Key | Purpose |
|---|---|---|
| Trigger | `find_object_names_singular` | Enumerates workspace object types (used to populate dynamic dropdowns) |
| Trigger | `list_record_ids` | Lists record IDs for a given object type |
| Trigger | `trigger_record` | Webhook trigger — fires on `created`, `updated`, `deleted`, or `destroyed` events for any object |
| Action | `crud_record` | Creates or updates a record of a selected object type |

The `trigger_record` component uses Zapier's `hook` type (REST webhooks). It calls `performSubscribe` / `performUnsubscribe` to register and deregister Twenty webhooks, and `performList` to backfill recent records.

Sources: [packages/twenty-zapier/src/index.ts:1-27](packages/twenty-zapier/src/index.ts), [packages/twenty-zapier/src/triggers/trigger_record.ts:10-59](packages/twenty-zapier/src/triggers/trigger_record.ts)

---

## TypeScript Client SDK (`packages/twenty-client-sdk`)

Twenty generates a workspace-specific TypeScript client at runtime via a background job. The generated client wraps `genql` (a type-safe GraphQL code generator) and injects `TwentyGeneratedClient` as a thin layer on top.

### `TwentyGeneratedClient`

The core class in `twenty-client-template.ts` handles:

- **Authorization token resolution**: checks `Authorization` header → `TWENTY_APP_ACCESS_TOKEN` env var → legacy `TWENTY_API_KEY` env var, in that priority order.
- **Transparent token refresh**: on a 401-style error, `executeGraphqlRequestWithOptionalRefresh` retries the request once with a refreshed token using a shared `refreshAccessTokenPromise` to prevent duplicate refresh calls.
- **File upload**: `uploadFile()` encodes the multipart GraphQL upload mutation (`UploadFilesFieldFileByUniversalIdentifier`) using the `FormData` / `map` protocol.
- **Custom fetch**: callers can inject their own `fetch` implementation for environments without `globalThis.fetch`.

```typescript
// packages/twenty-client-sdk/src/generate/twenty-client-template.ts
const APP_ACCESS_TOKEN_ENV_KEY = 'TWENTY_APP_ACCESS_TOKEN';
const API_KEY_ENV_KEY = 'TWENTY_API_KEY';

export class TwentyGeneratedClient {
  query<R extends QueryGenqlSelection>(request: R) { ... }
  mutation<R extends MutationGenqlSelection>(request: R) { ... }
  async uploadFile(fileBuffer, filename, contentType, fieldMetadataUniversalIdentifier) { ... }
}
```

### SDK Generation Job

The server generates per-application clients asynchronously via `GenerateSdkClientJob`, which delegates to `SdkClientGenerationService`. The job is dispatched when an application is provisioned or its schema changes.

Sources: [packages/twenty-client-sdk/src/generate/twenty-client-template.ts:34-190](packages/twenty-client-sdk/src/generate/twenty-client-template.ts), [packages/twenty-server/src/engine/core-modules/sdk-client/jobs/generate-sdk-client.job.ts:11-23](packages/twenty-server/src/engine/core-modules/sdk-client/jobs/generate-sdk-client.job.ts)

---

## Email Templates (`packages/twenty-emails`)

Transactional emails are React components rendered with [React Email](https://react.email). The `src/emails/` directory contains one component per email type:

| File | Purpose |
|---|---|
| `send-invite-link.email.tsx` | Workspace invitation |
| `send-email-verification-link.email.tsx` | Email address verification |
| `password-reset-link.email.tsx` | Password reset |
| `password-update-notify.email.tsx` | Password change notification |
| `warn-suspended-workspace.email.tsx` | Pre-suspension warning |
| `clean-suspended-workspace.email.tsx` | Post-suspension cleanup notice |
| `validate-approved-access-domain.email.tsx` | Domain approval confirmation |

The `EmailSenderJob` in the server picks up queued email tasks and renders + dispatches them via the configured SMTP driver (see Docker Compose environment variables).

---

## Connected Accounts: Messaging & Calendar Sync

Connected accounts are the mechanism by which Twenty links external provider credentials (Gmail, Microsoft, IMAP/SMTP/CalDAV) to workspace members. A `ConnectedAccountDTO` carries provider identity, tokens, and scopes.

```typescript
// packages/twenty-server/src/engine/metadata-modules/connected-account/dtos/connected-account.dto.ts
export class ConnectedAccountDTO {
  handle: string;           // email address
  provider: string;         // e.g. "google", "microsoft"
  accessToken: string | null;    // @HideField — never returned to clients
  refreshToken: string | null;   // @HideField
  scopes: string[] | null;
  connectionParameters: ImapSmtpCaldavParams | null;  // @HideField
}
```

### Messaging Sync

Incoming messages are imported via the `messagingQueue` (BullMQ). When a connected account is removed, `MessagingConnectedAccountDeletionCleanupJob` cleans orphaned messages and threads:

```typescript
// @Processor({ queueName: MessageQueue.messagingQueue, scope: Scope.REQUEST })
async handle(data): Promise<void> {
  await this.messageCleanerService.cleanOrphanMessagesAndThreads(data.workspaceId);
}
```

The workspace entity model (`MessageWorkspaceEntity`) stores subject, body text, received timestamp, and relations to threads, participants, and channel associations.

Sources: [packages/twenty-server/src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job.ts:1-30](packages/twenty-server/src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job.ts), [packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts:1-27](packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts)

### Calendar Sync

Calendar events are processed on the `calendarQueue`. Disconnecting an account dispatches `DeleteConnectedAccountAssociatedCalendarDataJob`, which calls `CalendarEventCleanerService.cleanWorkspaceCalendarEvents`:

```typescript
// @Processor(MessageQueue.calendarQueue)
async handle(data): Promise<void> {
  await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents(data.workspaceId);
}
```

Sources: [packages/twenty-server/src/modules/calendar/calendar-event-cleaner/jobs/delete-connected-account-associated-calendar-data.job.ts:1-25](packages/twenty-server/src/modules/calendar/calendar-event-cleaner/jobs/delete-connected-account-associated-calendar-data.job.ts)

### Provider Enablement

Providers are enabled via environment variables. All are commented out (disabled) by default in the Docker Compose files. Enable them by uncommenting and supplying credentials:

| Variable | Provider |
|---|---|
| `MESSAGING_PROVIDER_GMAIL_ENABLED` | Gmail sync |
| `CALENDAR_PROVIDER_GOOGLE_ENABLED` | Google Calendar |
| `MESSAGING_PROVIDER_MICROSOFT_ENABLED` | Microsoft mail |
| `CALENDAR_PROVIDER_MICROSOFT_ENABLED` | Microsoft calendar |
| `AUTH_GOOGLE_CLIENT_ID / SECRET` | Google OAuth |
| `AUTH_MICROSOFT_CLIENT_ID / SECRET` | Microsoft OAuth |

---

## Docker Compose: Dev and Production Deployments

Two Compose files ship in `packages/twenty-docker/`:

### `docker-compose.dev.yml` — Local Development Infrastructure

Starts Postgres 16 and Redis 7 only, with ports exposed to `localhost`. Application code runs locally from source. This is also what `packages/twenty-utils/setup-dev-env.sh` uses when the `--docker` flag is passed.

```yaml
services:
  db:    # postgres:16, port 5432
  redis: # redis:7, port 6379, --maxmemory-policy noeviction
```

Sources: [packages/twenty-docker/docker-compose.dev.yml:1-43](packages/twenty-docker/docker-compose.dev.yml)

### `docker-compose.yml` — Production / Full Stack

Runs the complete application stack from the `twentycrm/twenty:${TAG:-latest}` image:

```
┌─────────────────────────────────────────────────┐
│  server  (port 3000)  — NestJS + frontend        │
│  worker               — yarn worker:prod          │
│  db       postgres:16  — persistent volume        │
│  redis    redis        — noeviction               │
└─────────────────────────────────────────────────┘
```

Key design points:
- **server** and **worker** share the same image; the worker is distinguished solely by its command (`yarn worker:prod`).
- The worker sets `DISABLE_DB_MIGRATIONS=true` and `DISABLE_CRON_JOBS_REGISTRATION=true` — both are the server's responsibility.
- Storage can be local (volume `server-local-data`) or S3-compatible (`STORAGE_TYPE`, `STORAGE_S3_*` variables).
- Encryption keys (`ENCRYPTION_KEY`, `APP_SECRET`) are mandatory; messaging/calendar providers and SMTP are opt-in via commented variables.
- Health checks: server polls `/healthz`; Postgres uses `pg_isready`; Redis uses `redis-cli ping`.

Sources: [packages/twenty-docker/docker-compose.yml:1-138](packages/twenty-docker/docker-compose.yml)

---

## Extension Points

### Workspace Modules and Custom Objects

The workspace object system uses `BaseWorkspaceEntity` as the base class and decorates fields with `FieldMetadataType` enum values. New standard objects follow the pattern:
- Extend `BaseWorkspaceEntity` in a `.workspace-entity.ts` file.
- Declare relations using the `EntityRelation<T>` type.
- Register the module in the appropriate NestJS module with `@Processor` / `@Process` for any background jobs.
- Generate an instance command (`database:migrate:generate --name <name> --type fast|slow`) for schema changes.

### Adding a New Provider / External Connector

1. Add OAuth credentials and an enable flag to server environment (and Docker Compose env section).
2. Implement a `ConnectedAccount` variant by supplying `connectionParameters` (the `ImapSmtpCaldavParams` union or a new discriminant).
3. Register import and cleanup jobs on the appropriate `MessageQueue` channel.
4. Wire up `performSubscribe` / `performUnsubscribe` on the Zapier side if webhook-driven triggers are needed.

### Observability Add-ons

`packages/twenty-docker/` also ships Grafana dashboards, an OpenTelemetry Collector config (`otel-collector/`), Helm charts (`helm/`), Kubernetes manifests (`k8s/`), and Podman Compose files — providing operator-level extension points without changing application code.

---

## Summary

Twenty's external integration surfaces span a Zapier connector (webhook + CRUD actions over GraphQL), a generated type-safe TypeScript client SDK (per-application, with transparent token refresh), React Email-based transactional templates, and a connected-account system that syncs Gmail, Microsoft mail, and calendar data through dedicated BullMQ queues. Operationally, two Docker Compose files cover both local dev infrastructure (`docker-compose.dev.yml`) and full production stack (`docker-compose.yml`), with all provider integrations activated through environment variable feature flags rather than code changes.
