i18n - docs translations (#17036)

Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-01-09 17:26:06 +01:00
committed by GitHub
parent 109c47af68
commit 308973d7ca
58 changed files with 3585 additions and 3137 deletions
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## المصادقة
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | الغرض |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
الملاحظات:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: الأسئلة الشائعة حول الذكاء الاصطناعي
description: الأسئلة الشائعة حول ميزات الذكاء الاصطناعي في Twenty.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="متى ستكون ميزات الذكاء الاصطناعي متاحة؟">
ميزات الذكاء الاصطناعي قيد التطوير حالياً وسيتم إصدارها كإصدار تجريبي قريباً. ترقبوا التحديثات!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="ما هي القدرات المخطط لها للذكاء الاصطناعي؟">
نحن نطوّر قدرتين رئيسيتين للذكاء الاصطناعي:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **روبوت دردشة بالذكاء الاصطناعي**: مساعد مدرك للسياق يمكنه الوصول إلى بياناتك في Twenty ومساعدتك في الاستفسارات
2. **وكلاء الذكاء الاصطناعي في سير العمل**: أتمتة ذكية يمكنها معالجة البيانات واتخاذ القرارات وتنفيذ المهام ضمن سير عملك
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="هل سيكون لوكلاء الذكاء الاصطناعي حق الوصول إلى جميع بياناتي؟">
سيعمل وكلاء الذكاء الاصطناعي ضمن نظام الأذونات. يمكنك تعيين أدوار محددة لوكلاء الذكاء الاصطناعي ضمن **الإعدادات → الأدوار**، مما يمنحك سيطرة كاملة على البيانات التي يمكنهم الوصول إليها والإجراءات التي يمكنهم القيام بها.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="كيف ستعمل أرصدة الذكاء الاصطناعي؟">
ستستهلك إجراءات الذكاء الاصطناعي أرصدة سير العمل بناءً على تعقيد المهمة ونموذج الذكاء الاصطناعي المستخدم. ستتوفر المزيد من التفاصيل عند إطلاق الميزات.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="هل يمكنني استخدام نماذج الذكاء الاصطناعي الخاصة بي؟">
في البداية، ستستخدم Twenty نماذج ذكاء اصطناعي مدمجة. قد تتم إضافة دعم لنماذج الذكاء الاصطناعي المخصصة أو الخارجية في إصدارات مستقبلية استناداً إلى ملاحظات المستخدمين.
</Accordion>
</AccordionGroup>
@@ -180,22 +180,22 @@ Widget Co,https://widgets.co,widgets.co,New York,USA,50
</Accordion>
<Accordion title="هل يمكنني استيراد شركات دون ربط أي أشخاص بها؟">
نعم! You can import companies first, then import People later and link them using the company domain.
نعم! يمكنك استيراد الشركات أولاً، ثم استيراد الأشخاص لاحقًا وربطهم باستخدام نطاق الشركة.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="ماذا يحدث إذا قمتُ باستيراد نطاق موجود بالفعل؟">
إذا تضمّنت معرّفًا فريدًا (نطاقًا أو id) يطابق شركةً موجودة، يقوم Twenty بتحديث تلك الشركة بدلاً من إنشاء نسخة مكررة.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="كيف أصلِح أخطاء 'نطاق مكرر'؟">
إمّا أن تزيل النسخة المكررة من ملفك، أو تُدرج `id` الخاصة بالشركة لتحديث السجل الموجود بدلًا من ذلك.
</Accordion>
</AccordionGroup>
## استكشاف الأخطاء وإصلاحها
Having issues? Check:
هل تواجه مشكلات؟ اطّلع على:
* [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/ar/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
* [كيفية إصلاح أخطاء الاستيراد](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
* [مرجع تعيين الحقول](/l/ar/user-guide/data-migration/capabilities/field-mapping)
* [قيود التفرد](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: حقول العلاقات
description: Connect records across different objects using relation fields.
description: اربط السجلات عبر كائنات مختلفة باستخدام حقول العلاقة.
---
## Types of Relations
## أنواع العلاقات
### One-to-Many
### واحد-إلى-متعدد
One record in Object A can be linked to many records in Object B.
يمكن ربط سجل واحد في الكائن A بالعديد من السجلات في الكائن B.
**Example:** One Company can have many People (employees).
**مثال:** يمكن أن يكون لدى شركة واحدة العديد من الأشخاص (الموظفين).
### Many-to-One
### متعدد-إلى-واحد
Many records in Object A can be linked to one record in Object B.
يمكن ربط سجلات متعددة في الكائن A بسجل واحد في الكائن B.
**Example:** Many People can belong to one Company.
**مثال:** يمكن أن ينتمي العديد من الأشخاص إلى شركة واحدة.
### Relations to Multiple Object Types
### العلاقات إلى أنواع كائنات متعددة
Some objects can link to multiple object types on one side of the relation.
يمكن لبعض الكائنات الارتباط بأنواع كائنات متعددة على جانب واحد من العلاقة.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**مثال:** يمكن إرفاق ملاحظة بشخص واحد وشركة واحدة وفرصة واحدة في الوقت نفسه. الملاحظة على جانب "العديد"، وتتصل بعدة جوانب "الواحد".
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
وبالمثل، يمكن لمشروع (على جانب "الواحد") أن يتلقى روابط من عدة أشخاص، وعدة شركات، وعدة ملاحظات.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**قيد الاستيراد/التصدير**: العلاقات التي تشير إلى أنواع كائنات متعددة غير مدعومة بعد لاستيراد/تصدير CSV. هذا على خارطة الطريق لدينا.
</Warning>
### Many-to-Many
### متعدد-إلى-متعدد
Many records in Object A can be linked to many records in Object B.
يمكن ربط سجلات متعددة في الكائن A بسجلات متعددة في الكائن B.
**Example:** Many People can be linked to many Projects, and vice versa.
**مثال:** يمكن ربط العديد من الأشخاص بالعديد من المشاريع، والعكس صحيح.
<Warning>
**Many-to-Many is not yet supported.**
**متعدد-إلى-متعدد غير مدعوم بعد.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
هذا النوع من العلاقات مُخطّط للنصف الأول من عام 2026. كحل بديل، أنشئ كائنًا وسيطًا "junction" (مثال: "Project Assignments") لديه علاقات من نوع متعدد-إلى-واحد مع كلا الكائنين.
</Warning>
## Creating a Relation Field
## إنشاء حقل علاقة
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. اذهب إلى **الإعدادات → نموذج البيانات**
2. حدد الكائن الذي تريد إضافة العلاقة إليه
3. انقر **+ إضافة حقل**
4. اختر **العلاقة** كنوع الحقل
5. اختر الكائن(ات) الهدف للربط بها
6. كوّن إعدادات العلاقة:
* **اسم الحقل على الكائن المصدر**: اسم حقل العلاقة على الكائن الذي تقوم بتحريره
* **اسم الحقل على كائن الوجهة**: اسم حقل العلاقة الذي سيظهر على الكائن الهدف
* نوع العلاقة (واحد-إلى-متعدد، متعدد-إلى-واحد)
7. انقر على **حفظ**
## Standard Relations
## العلاقات القياسية
Twenty comes with pre-built relations between standard objects:
تأتي Twenty بعلاقات مبنية مسبقًا بين الكائنات القياسية:
| From Object | To Object | Relation Type |
| ----------- | --------- | ------------- |
| الأشخاص | الشركات | Many-to-One |
| الفرص | الشركات | Many-to-One |
| الفرص | الأشخاص | Many-to-One |
| من الكائن | إلى الكائن | نوع العلاقة |
| --------- | ---------- | -------------- |
| الأشخاص | الشركات | متعدد-إلى-واحد |
| الفرص | الشركات | متعدد-إلى-واحد |
| الفرص | الأشخاص | متعدد-إلى-واحد |
## أفضل الممارسات
### Planning Relations
### تخطيط العلاقات
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **ارسم خريطة نموذج البيانات**: خطط للعلاقات قبل إنشائها
* **ضع الاتجاه في الاعتبار**: فكّر في الكائن الذي "يمتلك" العلاقة
* **تجنّب الاعتماديات الدائرية**: حافظ على نظافة نموذج بياناتك
### Naming Relations
### تسمية العلاقات
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **استخدم أسماء واضحة**: اجعل ما تمثّله العلاقة واضحًا
* **كن متسقًا**: استخدم أنماط تسمية متشابهة عبر العلاقات
* **ضع كلا الجانبين في الاعتبار**: قم بتسمية كلا جانبي العلاقة بشكل مناسب
### Performance
### الأداء
* **Don't over-relate**: Too many relations can slow down your workspace
* **لا تفرط في إنشاء العلاقات**: كثرة العلاقات قد تبطئ مساحة عملك
## Limitations
## القيود
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **حذف العلاقات** يزيل الرابط لكنه لا يزيل السجلات المرتبطة
* **العلاقات الدائرية** ينبغي تجنبها للحفاظ على سلامة البيانات
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: إنشاء حقول مخصصة
description: دليل خطوة بخطوة لإضافة حقول مخصصة إلى أي كائن.
---
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
تمكّنك الحقول المخصصة من جمع المعلومات الخاصة بنشاطك التجاري. أضفها إلى أي كائن—قياسي أو مخصص.
## Steps
## الخطوات
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/ar/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. اذهب إلى **الإعدادات → نموذج البيانات**
2. حدد الكائن الذي تريد إضافة حقل إليه
3. انقر **+ إضافة حقل**
4. اختر **نوع الحقل** (راجع [الحقول](/l/ar/user-guide/data-model/capabilities/fields) للاطلاع على جميع الأنواع)
5. أدخل **اسم الحقل** ووصفًا اختياريًا
6. قم بتهيئة الإعدادات الخاصة بالحقل (انظر أدناه)
7. انقر على **حفظ**
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**طريقة سريعة:** انقر **+** في نهاية عناوين الأعمدة في أي عرض جدولي → **تخصيص الحقول**.
## Show the Field in Views
## إظهار الحقل في العروض
New fields aren't automatically visible. To display:
لا تظهر الحقول الجديدة تلقائيًا. للعرض:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. افتح العرض الجدولي للكائن
2. انقر على **الخيارات → الحقول**
3. انقر على **أيقونة العين** بجانب الحقل لإظهاره
4. اسحب لإعادة الترتيب
## Configuration Options
## خيارات التهيئة
### For Select / Multi-Select
### لحقول الاختيار/الاختيار المتعدد
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. انقر **+ إضافة خيار** لإنشاء خيارات
2. عيّن **الخيار الافتراضي** إذا رغبت
3. اسحب لإعادة ترتيب الخيارات
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping).
**استخدم أسماء واجهة برمجة التطبيقات (API) لعمليات الاستيراد.** فعّل **الوضع المتقدم** في الإعدادات لعرض أسماء واجهة برمجة التطبيقات. راجع [تعيين الحقول](/l/ar/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### لحقول العملة
Set the **default currency** (USD, EUR, etc.) for new records.
عيّن **العملة الافتراضية** (USD وEUR وما إلى ذلك) للسجلات الجديدة.
### For Phone Fields
### لحقول الهاتف
Set the **default country code** to pre-fill for new phone numbers.
عيّن **رمز البلد الافتراضي** ليتم ملؤه مسبقًا للأرقام الهاتفية الجديدة.
### Making a Field Unique
### جعل الحقل فريدًا
Toggle **Unique** to prevent duplicate values across records.
فعّل **فريد** لمنع تكرار القيم عبر السجلات.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
إذا وُجدت قيم مكررة (بما في ذلك في السجلات المحذوفة)، ستظهر رسالة خطأ. أزل التكرارات أولًا.
</Note>
### Setting Default Values
### تعيين القيم الافتراضية
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
في حقول الاختيار، يمكنك تحديد الخيار الذي سيتم اختياره مسبقًا للسجلات الجديدة. في حقول خانة الاختيار، حدّد ما إذا كانت محددة أم غير محددة افتراضيًا.
## Deactivating a Field
## إلغاء تنشيط حقل
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. اذهب إلى **الإعدادات → نموذج البيانات**
2. اعثر على الحقل
3. انقر **⋮ → إلغاء التنشيط**
Data is preserved. You can reactivate or permanently delete later.
تظل البيانات محفوظة. يمكنك إعادة التنشيط أو الحذف نهائيًا لاحقًا.
## Related
## ذات صلة
* [Fields](/l/ar/user-guide/data-model/capabilities/fields) — all field types explained
* [Data Model FAQ](/l/ar/user-guide/data-model/how-tos/data-model-faq) — common questions
* [الحقول](/l/ar/user-guide/data-model/capabilities/fields) — شرح جميع أنواع الحقول
* [الأسئلة الشائعة حول نموذج البيانات](/l/ar/user-guide/data-model/how-tos/data-model-faq) — أسئلة شائعة
@@ -1,6 +1,6 @@
---
title: نموذج البيانات
description: Learn what a data model is and how to design one that fits your business.
description: تعرّف إلى نموذج البيانات وكيفية تصميم نموذج يناسب نشاطك التجاري.
image: /images/user-guide/fields/custom_data_model.png
---
@@ -8,120 +8,120 @@ image: /images/user-guide/fields/custom_data_model.png
<img src="/images/user-guide/fields/custom_data_model.png" alt="نموذج البيانات" />
</Frame>
## What is a Data Model?
## ما هو نموذج البيانات؟
A data model is the structure that defines how information is organized in your CRM. Think of it as the **blueprint** of your customer data — you design it once, then fill it with your actual data.
نموذج البيانات هو الهيكل الذي يحدّد كيفية تنظيم المعلومات في نظام إدارة علاقات العملاء (CRM) لديك. فكّر فيه باعتباره **المخطط** لبيانات عملائك — تصمّمه مرة واحدة، ثم تملؤه ببياناتك الفعلية.
## Key Concepts
## المفاهيم الأساسية
### كائنات
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
**الكائنات** هي الفئات الرئيسية للبيانات في نظام إدارة علاقات العملاء (CRM) لديك. يمثّل كل كائن نوعًا من الأشياء التي تريد تتبّعها.
Twenty comes with standard objects:
يوفّر Twenty كائنات قياسية:
* **People** — individuals (contacts, leads, partners)
* **Companies** — organizations
* **Opportunities** — deals or sales
* **Notes** — attached notes on records
* **Tasks** — to-dos linked to records
* **الأشخاص** — أفراد (جهات اتصال، عملاء محتملون، شركاء)
* **الشركات** — مؤسسات
* **الفرص** — صفقات أو مبيعات
* **الملاحظات** — ملاحظات مرفقة بالسجلات
* **المهام** — عناصر قائمة المهام المرتبطة بالسجلات
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
يمكنك أيضًا إنشاء **كائنات مخصّصة** لأي شيء خاص بنشاطك التجاري (مثل: المشروعات، الاشتراكات، الأحداث).
### الحقول
**Fields** are the properties or attributes that describe each object. They store the actual information.
**الحقول** هي الخصائص أو السمات التي تصف كل كائن. إنها تخزّن المعلومات الفعلية.
For example, the **People** object has fields like:
على سبيل المثال، يحتوي كائن **الأشخاص** على حقول مثل:
* الاسم
* البريد الإلكتروني
* هاتف
* المسمى الوظيفي
* Company (a relation to the Companies object)
* الشركة (علاقة بكائن الشركات)
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
للحقول **أنواع** مختلفة: نص، رقم، تاريخ، اختيار، اختيار متعدد، علاقة، وغير ذلك. يمكنك إضافة حقول مخصّصة إلى أي كائن.
### السجلات
**Records** are the individual entries within an object — the actual data you create and manage.
**السجلات** هي الإدخالات الفردية داخل الكائن — البيانات الفعلية التي تنشئها وتديرها.
على سبيل المثال:
* "John Smith" is a **record** in the People object
* "Acme Corp" is a **record** in the Companies object
* "John Smith" هو **سجل** في كائن الأشخاص
* "Acme Corp" هو **سجل** في كائن الشركات
**An analogy:**
**تشبيه:**
| Data Model Concept | Real-World Analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **حقول** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
| مفهوم نموذج البيانات | تشبيه من الواقع |
| -------------------- | --------------------------------------- |
| **الكائنات** | أقسام في كتاب (الفئات) |
| **حقول** | أعمدة في جدول بيانات (الخصائص) |
| **السجلات** | صفوف في جدول بيانات (الإدخالات الفعلية) |
You design the data model (objects + fields) once, then create many records within that structure.
تصمّم نموذج البيانات (الكائنات + الحقول) مرة واحدة، ثم تنشئ العديد من السجلات ضمن تلك البنية.
## Why Customize Your Data Model?
## لماذا تخصّص نموذج البيانات الخاص بك؟
كل شركة تعمل بطريقة مختلفة. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
كل شركة تعمل بطريقة مختلفة. يعني تخصيص نموذج البيانات أنك تستطيع تكييف Twenty حول عملياتك بدلاً من فرض عملياتك داخل نظام جامد.
Twenty offers full flexibility:
توفر Twenty مرونة كاملة:
* Create as many custom objects as you need
* Add unlimited custom fields
* The price doesn't change based on customization
* أنشئ عددًا من الكائنات المخصّصة بقدر ما تحتاج
* أضِف حقولًا مخصّصة غير محدودة
* لا يتغيّر السعر بناءً على التخصيص
## Tips to Design Your Data Model
## نصائح لتصميم نموذج البيانات الخاص بك
### 1. Start with Your Core Objects
### 1. ابدأ بكائناتك الأساسية
Identify the main concepts you work with. Twenty already provides:
حدّد المفاهيم الرئيسية التي تعمل بها. توفّر Twenty مسبقًا:
* **People** — your contacts
* **Companies** — your accounts
* **Opportunities** — your deals
* **الأشخاص** — جهات الاتصال لديك
* **الشركات** — حساباتك
* **الفرص** — صفقاتك
Think about what else you might need:
فكّر فيما قد تحتاج إليه أيضًا:
* Stripe would need a `Subscriptions` object
* Airbnb would need a `Trips` object
* An accelerator would need a `Batches` object
* ستحتاج Stripe إلى كائن `Subscriptions`
* ستحتاج Airbnb إلى كائن `Trips`
* ستحتاج مُسرِّعة أعمال إلى كائن `Batches`
### ٢. Use Fields for Variations, Not New Objects
### ٢. استخدم الحقول للاختلافات، وليس كائنات جديدة
If something is just a characteristic of an existing object, make it a **field**.
إذا كان الشيء مجرد سمة لكائن موجود، فاجعله **حقلًا**.
**Use fields for:**
**استخدم الحقول من أجل:**
* Categories and labels (e.g., `Industry` for Companies)
* Status values (e.g., `Stage` for Opportunities)
* Attributes and properties
* الفئات والوسوم (مثل `Industry` للشركات)
* قيم الحالة (مثل `Stage` للفرص)
* السمات والخصائص
### ٣. Create an Object When It Stands on Its Own
### ٣. أنشئ كائنًا عندما يكون قائمًا بذاته
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
إذا كان للمفهوم دورة حياة أو خصائص أو علاقات خاصة به، فهو يستحق كائنًا مستقلًا.
**Create an object for:**
**أنشئ كائنًا من أجل:**
* **Projects** — have deadlines, owners, and tasks
* **Subscriptions** — connect companies, products, and invoices
* **Events** — involve attendees and follow-up actions
* **المشروعات** — لها مواعيد نهائية ومالكون ومهام
* **الاشتراكات** — تربط بين الشركات والمنتجات والفواتير
* **الأحداث** — تتضمن الحضور وإجراءات المتابعة
تتجاوز هذه الأشياء مجرد ما يمكن تضمينه في حقل واحد لأنها تحمل بياناتها وعلاقاتها الخاصة.
### 4. Create an Object When Records Are Open-Ended
### 4. أنشئ كائنًا عندما تكون السجلات مفتوحة وغير محددة العدد
If something can be linked multiple times and you don't know how many, use an object.
إذا كان بالإمكان ربط الشيء مراتٍ متعددة ولا تعرف عددها، فاستخدم كائنًا.
**Bad approach:**
Creating fields like `Product 1`, `Product 2`, `Product 3`...
**نهج غير جيد:**
إنشاء حقول مثل `Product 1`، `Product 2`، `Product 3`...
**Good approach:**
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
**نهج جيد:**
أنشئ كائنًا `Products` واربطه بالسجلات. يسمح ذلك بدعم منتج واحد أو اثنين أو حتى مائة منتج دون تغيير نموذجك.
### 5. Keep It Simple First
### 5. حافظ على البساطة أولًا
Start with fields. Move to new objects only when you feel the limits:
ابدأ بالحقول. انتقل إلى كائنات جديدة فقط عندما تشعر بالقيود:
* Too many fields on one object
* Repeated records that should be separate
@@ -1,9 +1,9 @@
---
title: إعدادات مساحة العمل
description: Customize your workspace name and branding.
description: خصص اسم مساحة العمل وعلامتها التجارية.
---
Those are accessible under **Settings → General**.
يمكن الوصول إليها ضمن **الإعدادات → عام**.
## صورة مساحة العمل
@@ -16,7 +16,7 @@ Those are accessible under **Settings → General**.
* **الاسم**: تغيير اسم العرض لمساحة العمل
* يظهر هذا الاسم لجميع أعضاء مساحة العمل
## Danger Zone
## المنطقة الخطرة
<Warning>
حذف مساحة العمل الخاصة بك سيقوم بإزالة جميع البيانات بشكل دائم ولن يمكن التراجع عن ذلك. سوف يتم فقد جميع بيانات مساحة العمل للأبد، سيفقد جميع الأعضاء حق الوصول فورًا، ولن يمكن عكس هذا الإجراء.
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: الحقول والأعمدة
description: اختر الحقول التي تريد عرضها وكيفية تنظيمها.
---
## Selecting Fields to Display
## تحديد الحقول لعرضها
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
يمكن لكل عرض أن يعرض مجموعة مختلفة من الحقول. خصّص ما هو مرئي للتركيز على المعلومات المهمة.
### Show or Hide Fields
### إظهار الحقول أو إخفاؤها
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. انقر على **الخيارات** في أعلى اليمين
2. انقر على **الحقول**
3. انقر على **أيقونة العين** بجانب كل حقل لإظهاره/إخفائه
### Reorder Fields
### إعادة ترتيب الحقول
Change the order fields appear in your view:
غيّر ترتيب ظهور الحقول في عرضك:
1. Click **Options → Fields**
2. Drag fields up or down
3. Changes save automatically
1. انقر على **الخيارات → الحقول**
2. اسحب الحقول لأعلى أو لأسفل
3. يتم حفظ التغييرات تلقائيًا
## Field Display by View Type
## عرض الحقول حسب نوع العرض
### عرض الجداول
* Fields appear as columns
* Resize columns by dragging borders
* تظهر الحقول كأعمدة
* غيّر حجم الأعمدة بسحب الحدود
### عرض كانبان‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* تظهر الحقول على البطاقات
* أعِد الترتيب عبر الخيارات → الحقول
* استخدم العرض المضغوط لإخفاء جميع الحقول
### Calendar Views
### عروض التقويم
* Selected fields show on calendar events
* Configure via Options → Fields
* تظهر الحقول المحددة في أحداث التقويم
* قم بالتهيئة عبر الخيارات → الحقول
## أفضل الممارسات
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **اعرض ما هو مطلوب فقط** — كثرة الحقول تجعل العرض مزدحمًا
* **ضع الحقول المهمة أولاً** — الأعمدة الأكثر استخدامًا على اليسار
* **أنشئ عدة عروض** — مجموعات حقول مختلفة لأغراض مختلفة
* استخدم إعدادات ظهور الحقول لكل عرض — الكائن نفسه، تركيز مختلف
## Related
## ذات صلة
* [Table Views](/l/ar/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/ar/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [عروض الجدول](/l/ar/user-guide/views-pipelines/capabilities/table-views) — ميزات عرض القائمة
* [عروض كانبان](/l/ar/user-guide/views-pipelines/capabilities/kanban-views) — عروض قائمة على البطاقات
@@ -1,6 +1,6 @@
---
title: Kanban Board Views
description: Learn how to use Kanban views to visualize and manage your workflows.
title: طرق عرض لوحة كانبان
description: تعرّف على كيفية استخدام طرق عرض كانبان لتصوّر وإدارة سير العمل لديك.
image: /images/user-guide/kanban-views/kanban.png
---
@@ -10,13 +10,13 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
توضح عروض كانبان التدفقات العملية بشكل مرئي، حيث يرمز كل عمود إلى مرحلة محددة وكل بطاقة تمثل سجل.
## Move Cards between Stages
## انقل البطاقات بين المراحل
يمكنك نقل كل بطاقة بين المراحل بينما تتقدم عبر سير العمل الخاص بك عن طريق السحب والإفلات. للمتابعة، اضغط باستمرار على البطاقة وانقلها إلى المرحلة التالية.‏
<VimeoEmbed videoId="927888627" title="Video demonstration" />
<VimeoEmbed videoId="927888627" title="عرض توضيحي بالفيديو" />
## Add and Delete Stages
## إضافة المراحل وحذفها
يمكنك تخصيص سير العمل لديك ليتناسب مع احتياجاتك باستخدام المراحل، والتي تمثل قيمة في حقل التحديد:
@@ -24,15 +24,15 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
لإضافة مرحلة، انتقل إلى إعدادات حقل الاختيار عن طريق الانتقال إلى الإعدادات > نموذج البيانات، وتحديد الكائن، ثم الحقل الذي يعتمد عليه لوحة كانبان الخاصة بك.‏
<VimeoEmbed videoId="927890428" title="Video demonstration" />
<VimeoEmbed videoId="927890428" title="عرض توضيحي بالفيديو" />
### إزالة مراحل
To remove a stage, hover the stage name or the `⋮` icon, click `Edit from settings` in the Select field settings, and then click **Delete** next to the relevant stage.
لإزالة مرحلة، مرّر مؤشر الفأرة فوق اسم المرحلة أو أيقونة `⋮`، وانقر على `تحرير من الإعدادات` في إعدادات حقل التحديد، ثم انقر على **حذف** بجوار المرحلة ذات الصلة.
## Display Fields
## حقول العرض
يمكنك تكوين لوحة عرض كانبان لإظهار بعض الحقول وإخفاء الأخرى. To hide a field, click on **Options** on the top right, then on **Fields** to bring up the list of options. Look for the field needed in the Hidden Fields section and click on the eye button to display the field.
يمكنك تكوين لوحة عرض كانبان لإظهار بعض الحقول وإخفاء الأخرى. لإخفاء أحد الحقول، انقر على **الخيارات** في أعلى اليمين، ثم على **الحقول** لإظهار قائمة الخيارات. ابحث عن الحقل المطلوب في قسم الحقول المخفية وانقر على زر العين لعرض الحقل.
يمكنك أيضًا إعادة ترتيب ترتيب الحقول عن طريق الضغط باستمرار على اسم الحقل وسحبه إلى المكان الذي تريده.
@@ -40,60 +40,60 @@ To remove a stage, hover the stage name or the `⋮` icon, click `Edit from sett
## عرض مضغوط
You can hide all the fields and get an overview of all records at a glance. To enable:
يمكنك إخفاء جميع الحقول والحصول على لمحة عامة عن جميع السجلات. للتفعيل:
1. Click **Options** on the top right
2. Turn on the toggle for **Compact view**
1. انقر على **الخيارات** في أعلى اليمين
2. فعِّل مفتاح التبديل لخيار **العرض المضغوط**
<img src="/images/user-guide/kanban-views/compact-view.png" style={{width:'100%'}} />
## Column Aggregations
## تجميعات الأعمدة
Each column in a Kanban view can display aggregated values at the top, helping you understand your data at a glance.
يمكن لكل عمود في طريقة عرض كانبان عرض قيم مُجمَّعة في الأعلى، مما يساعدك على فهم بياناتك بنظرة سريعة.
### Available Aggregations
### التجميعات المتاحة
| Aggregation | الوصف |
| ----------- | --------------------------------------------- |
| **Count** | Number of records in the column |
| **Sum** | Total of a numeric field (e.g., deal amounts) |
| **Average** | Average value of a numeric field |
| **Min** | Lowest value |
| **Max** | Highest value |
| التجميع | الوصف |
| ----------- | ----------------------------------- |
| **العدد** | عدد السجلات في العمود |
| **المجموع** | إجمالي حقل رقمي (مثل مبالغ الصفقات) |
| **المتوسط** | القيمة المتوسطة لحقل رقمي |
| **الأدنى** | أقل قيمة |
| **الأقصى** | أعلى قيمة |
### Configuring Aggregations
### تكوين التجميعات
1. Click on the number displayed next to the Stage value, at the top of a column
2. Select the aggregation type
3. Choose the field to aggregate
1. انقر على الرقم المعروض بجوار قيمة المرحلة، في أعلى العمود
2. اختر نوع التجميع
3. اختر الحقل المراد تجميعه
**Example:** Show total deal value per stage by aggregating the Amount field with Sum.
**مثال:** اعرض إجمالي قيمة الصفقات لكل مرحلة من خلال تجميع حقل Amount باستخدام المجموع.
## When to Use Kanban Views
## متى يجب استخدام طرق عرض كانبان
Kanban views are ideal for:
طرق عرض كانبان مثالية لـ:
* **Sales pipelines**: Track deals through stages from lead to close
* **Project management**: Monitor tasks through workflow states
* **Recruitment**: Track candidates through hiring stages
* **Any staged process**: Visualize any workflow with defined stages
* **مسارات المبيعات**: تتبّع الصفقات عبر المراحل من العميل المحتمل حتى الإغلاق
* **إدارة المشاريع**: راقِب المهام عبر حالات سير العمل
* **التوظيف**: تتبّع المرشحين عبر مراحل التوظيف
* **أي عملية مرحلية**: تصوّر أي سير عمل بمراحل محدّدة
## أفضل الممارسات
### Organize Your Stages
### نظّم مراحلَك
* **Limit stages**: 5-7 stages is ideal for visibility
* **Clear naming**: Use descriptive stage names
* **Logical order**: Arrange stages in process order
* **حدّد عدد المراحل**: من 5 إلى 7 مراحل مناسب للرؤية الواضحة
* **تسمية واضحة**: استخدم أسماء مراحل وصفية
* **ترتيب منطقي**: رتّب المراحل وفق ترتيب العملية
### Optimize Card Display
### حسّن عرض البطاقات
* **Show key fields**: Display only the most important information
* **Use compact view**: For high-level overviews
* **Color coding**: Use stage colors to quickly identify status
* **أظهر الحقول الرئيسية**: اعرض المعلومات الأهم فقط
* **استخدم العرض المضغوط**: للحصول على لمحات عامة عالية المستوى
* **الترميز اللوني**: استخدم ألوان المراحل للتعرّف السريع على الحالة
### Maintain Data Quality
### حافظ على جودة البيانات
* **Update regularly**: Keep cards moving through stages
* **Archive completed**: Move closed items out of active view
* **Review stale cards**: Follow up on cards stuck in stages
* **حدّث بانتظام**: أبقِ البطاقات تتحرك عبر المراحل
* **أرشِف العناصر المكتملة**: انقل العناصر المغلقة خارج طريقة العرض النشطة
* **راجع البطاقات الراكدة**: تابِع البطاقات العالقة في المراحل
@@ -1,140 +1,140 @@
---
title: Closed Won Automations
description: Automate post-win activities when opportunities close.
title: أتمتة الصفقات المغلقة الرابحة
description: أتمتة الأنشطة بعد الفوز عند إغلاق الفرص.
---
When a deal closes, multiple things need to happen: update company status, notify team members, create onboarding tasks. Automate all of this with a single workflow.
عند إغلاق صفقة، يجب أن تحدث أمور عدة: تحديث حالة الشركة، إشعار أعضاء الفريق، إنشاء مهام الإعداد. قم بأتمتة كل ذلك عبر سير عمل واحد.
## The Problem
## المشكلة
When an opportunity moves to "Closed Won":
عند انتقال فرصة إلى "Closed Won":
* Company type needs to change from "Prospect" to "Customer"
* Onboarding tasks need to be created
* Customer success team needs to be notified
* Sales rep needs confirmation
* يجب تغيير نوع الشركة من "Prospect" إلى "Customer"
* يجب إنشاء مهام الإعداد
* يجب إشعار فريق نجاح العملاء
* يحتاج مندوب المبيعات إلى تأكيد
Doing this manually is time-consuming and error-prone.
القيام بذلك يدويًا يستغرق وقتًا ومعرّض للأخطاء.
## The Solution
## الحل
Create a workflow that handles all post-win activities automatically.
أنشئ سير عمل يتولى تلقائيًا جميع الأنشطة بعد الفوز.
## Complete Workflow Setup
## إعداد سير العمل الكامل
### Step 1: Create the Workflow
### الخطوة 1: إنشاء سير العمل
1. Go to **Settings → Workflows**
2. Click **+ New Workflow**
3. Name it "Deal Won - Post-Win Automation"
1. اذهب إلى **الإعدادات → سير العمل**
2. انقر **+ سير عمل جديد**
3. قم بتسميته "تم ربح الصفقة - أتمتة ما بعد الفوز"
### Step 2: Configure the Trigger
### الخطوة 2: تهيئة المشغّل
1. Select **Record is Updated**
2. Choose **Opportunities**
3. Under "Fields to monitor", select **Stage**
1. اختر **Record is Updated**
2. اختر **Opportunities**
3. ضمن "Fields to monitor"، اختر **Stage**
### Step 3: Add Stage Filter
### الخطوة 3: إضافة عامل تصفية للمرحلة
1. Add **Filter** action
2. Condition: `{{trigger.object.stage}}` equals "Closed Won"
1. أضف إجراء **Filter**
2. الشرط: `{{trigger.object.stage}}` يساوي "Closed Won"
### Step 4: Update Company Type
### الخطوة 4: تحديث نوع الشركة
1. Add **Update Record** action
2. Configure:
1. أضف إجراء **Update Record**
2. التكوين:
| الحقل | القيمة |
| ------------------- | ------------------------------- |
| **Object** | الشركات |
| **Record** | `{{trigger.object.company.id}}` |
| **نوع** | العميل |
| **First Deal Date** | `{{trigger.object.closedAt}}` |
| **مالك الحساب** | `{{trigger.object.owner.id}}` |
| الحقل | القيمة |
| ------------------ | ------------------------------- |
| **الكائن** | الشركات |
| **السجل** | `{{trigger.object.company.id}}` |
| **نوع** | العميل |
| **تاريخ أول صفقة** | `{{trigger.object.closedAt}}` |
| **مالك الحساب** | `{{trigger.object.owner.id}}` |
### Step 5: Create Onboarding Task
### الخطوة 5: إنشاء مهمة إعداد
1. Add **Create Record** action
2. Configure:
1. أضف إجراء **Create Record**
2. التكوين:
| الحقل | القيمة |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Object** | المهام |
| **Title** | `Onboarding: {{trigger.object.name}}` |
| **Assignee** | Customer Success team member |
| **Due Date** | 3 days from now |
| **Priority** | High |
| **Related Company** | `{{trigger.object.company.id}}` |
| **Related Opportunity** | `{{trigger.object.id}}` |
| **Description** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
| الحقل | القيمة |
| -------------------- | ----------------------------------------------------------------------------------------------- |
| **الكائن** | المهام |
| **العنوان** | `Onboarding: {{trigger.object.name}}` |
| **المسند إليه** | عضو فريق نجاح العملاء |
| **تاريخ الاستحقاق** | بعد 3 أيام |
| **الأولوية** | عالية |
| **الشركة ذات الصلة** | `{{trigger.object.company.id}}` |
| **الفرصة ذات الصلة** | `{{trigger.object.id}}` |
| **الوصف** | `إعداد عميل جديد لشركة {{trigger.object.company.name}}. قيمة الصفقة: {{trigger.object.amount}}` |
### Step 6: Notify Customer Success
### الخطوة 6: إشعار فريق نجاح العملاء
1. Add **Send Email** action
2. Configure:
1. أضف إجراء **Send Email**
2. التكوين:
| الحقل | القيمة |
| ----------- | -------------------------------------------------- |
| **To** | customer-success@yourcompany.com |
| **Subject** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Body** | See example below |
| الحقل | القيمة |
| ----------- | ----------------------------------------------- |
| **إلى** | customer-success@yourcompany.com |
| **الموضوع** | `🎉 عميل جديد: {{trigger.object.company.name}}` |
| **المحتوى** | انظر المثال أدناه |
**Email body example**:
**مثال على محتوى البريد الإلكتروني**:
```
Hi CS Team,
مرحبًا فريق نجاح العملاء،
We have a new customer!
لدينا عميل جديد!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
الشركة: {{trigger.object.company.name}}
الصفقة: {{trigger.object.name}}
القيمة: {{trigger.object.amount}}
مندوب المبيعات: {{trigger.object.owner.name}}
تاريخ الإغلاق: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
تم إنشاء مهمة الإعداد تلقائيًا.
Let's give them a great start!
لنمنحهم بداية رائعة!
```
### Step 7: Confirm to Sales Rep
### الخطوة 7: تأكيد لمندوب المبيعات
1. Add another **Send Email** action
2. Configure:
1. أضف إجراء **Send Email** آخر
2. التكوين:
| الحقل | القيمة |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| **To** | `{{trigger.object.owner.email}}` |
| **Subject** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Body** | Congratulations! Your deal has been processed. The customer success team has been notified and onboarding has begun. |
| الحقل | القيمة |
| ----------- | -------------------------------------------------------------------------- |
| **إلى** | `{{trigger.object.owner.email}}` |
| **الموضوع** | `✅ تم إغلاق الصفقة: {{trigger.object.name}}` |
| **المحتوى** | تهانينا! تمت معالجة صفقتك. تم إشعار فريق نجاح العملاء وبدأت عملية الإعداد. |
### Step 8: Test and Activate
### الخطوة 8: الاختبار والتفعيل
1. Test by moving a test opportunity to "Closed Won"
1. اختبر بنقل فرصة اختبار إلى "Closed Won"
2. التحقق:
* Company type changed to "Customer"
* Onboarding task created
* CS team received email
* Sales rep received confirmation
3. Activate when ready
* تم تغيير نوع الشركة إلى "Customer"
* تم إنشاء مهمة الإعداد
* تلقى فريق نجاح العملاء بريدًا إلكترونيًا
* تلقى مندوب المبيعات تأكيدًا
3. فعّل عند الجاهزية
## Handling Closed Lost
## التعامل مع "Closed Lost"
Create a similar workflow for lost deals:
أنشئ سير عمل مشابهًا للصفقات الخاسرة:
### Trigger
### المشغّل
* Record is Updated (Opportunities, Stage = "Closed Lost")
* تم تحديث السجل (Opportunities، Stage = "Closed Lost")
### الإجراءات
1. **Create Record**: Task for "Lost Deal Analysis"
2. **Update Record**: Add lost reason to company record
3. **Send Email**: Notify manager of lost deal
1. **Create Record**: مهمة "تحليل الصفقة الخاسرة"
2. **Update Record**: أضِف سبب الخسارة إلى سجل الشركة
3. **Send Email**: إخطار المدير بالصفقة الخاسرة
## Advanced: Multi-Step Onboarding
## متقدم: إعداد متعدد الخطوات
For complex onboarding, create multiple tasks:
لعمليات الإعداد المعقدة، أنشئ مهام متعددة:
```javascript
export const main = async (params) => {
@@ -149,31 +149,31 @@ export const main = async (params) => {
};
```
Use **Iterator** to create each task from the array.
استخدم **Iterator** لإنشاء كل مهمة من المصفوفة.
## Customization Ideas
## أفكار للتخصيص
### Keep your other tools up-to-date
### أبقِ أدواتك الأخرى محدثة
* Create customer in billing system with an **HTTP Request**
* أنشئ عميلًا في نظام الفوترة بواسطة **HTTP Request**
### Conditional Actions
### إجراءات شرطية
Use **Filter** actions to:
استخدم إجراءات **Filter** لـ:
* Different onboarding for enterprise vs SMB
* Different assignees based on region
* Skip notifications for small deals
* إعداد مختلف للمؤسسات مقابل الشركات الصغيرة والمتوسطة
* مُسندون مختلفون حسب المنطقة
* تخطي الإشعارات للصفقات الصغيرة
### Include Deal Details
### تضمين تفاصيل الصفقة
Use **Code** action to format:
استخدم إجراء **Code** للتنسيق:
* Deal summary documents
* Handoff notes for CS team
* Custom onboarding checklists
* مستندات ملخص الصفقة
* ملاحظات التسليم لفريق نجاح العملاء
* قوائم تدقيق إعداد مخصصة
## Related
## ذات صلة
* [Workflow Actions](/l/ar/user-guide/workflows/capabilities/workflow-actions)
* [Send Emails from Workflows](/l/ar/user-guide/workflows/capabilities/send-emails-from-workflows)
* [إجراءات سير العمل](/l/ar/user-guide/workflows/capabilities/workflow-actions)
* [إرسال رسائل البريد الإلكتروني من سير العمل](/l/ar/user-guide/workflows/capabilities/send-emails-from-workflows)
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Ověření
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Účel |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Poznámky:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: Často kladené otázky k AI
description: Často kladené otázky k funkcím AI v Twenty.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="Kdy budou funkce AI k dispozici?">
Funkce AI jsou právě ve vývoji a brzy budou vydány v beta verzi. Sledujte aktualizace!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="Jaké možnosti AI jsou plánovány?">
Budujeme dvě hlavní možnosti AI:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **AI Chatbot**: Asistent s kontextovým povědomím, který může přistupovat k vašim datům v Twenty a pomůže vám s dotazy
2. **AI agenti v pracovních postupech**: Inteligent automatizace, která dokáže zpracovávat data, činit rozhodnutí a vykonávat úlohy ve vašich pracovních postupech
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="Budou mít AI agenti přístup ke všem mým datům?">
AI agenti budou fungovat v rámci systému oprávnění. V části **Nastavení → Role** můžete AI agentům přiřadit konkrétní role, čímž získáte plnou kontrolu nad tím, k jakým datům mají přístup a jaké akce mohou provádět.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="Jak budou fungovat kredity pro AI?">
Akce AI budou spotřebovávat kredity pracovních postupů podle složitosti úlohy a použitého modelu AI. Více podrobností bude k dispozici při spuštění funkcí.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="Mohu používat vlastní modely AI?">
Zpočátku bude Twenty používat vestavěné modely AI. Podpora vlastních nebo externích modelů AI může být přidána v budoucích verzích na základě zpětné vazby od uživatelů.
</Accordion>
</AccordionGroup>
@@ -180,22 +180,22 @@ Podrobnosti viz [Jak aktualizovat existující záznamy](/l/cs/user-guide/data-m
</Accordion>
<Accordion title="Mohu importovat společnosti bez jakýchkoli propojených Osob?">
Ano! You can import companies first, then import People later and link them using the company domain.
Ano! Nejprve můžete importovat společnosti, poté importovat osoby a propojit je pomocí firemní domény.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="Co se stane, když naimportuji doménu, která už existuje?">
Pokud zahrnete jedinečný identifikátor (doménu nebo id), který odpovídá existující společnosti, Twenty tuto společnost aktualizuje místo vytvoření duplikátu.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="Jak opravím chyby 'duplicate domain'?">
Buď odstraňte duplikát ze svého souboru, nebo uveďte `id` společnosti, abyste místo toho aktualizovali existující záznam.
</Accordion>
</AccordionGroup>
## Řešení potíží
Having issues? Check:
Máte potíže? Podívejte se na:
* [How to Fix Import Errors](/l/cs/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/cs/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/cs/user-guide/data-migration/capabilities/uniqueness-constraints)
* [Jak opravit chyby importu](/l/cs/user-guide/data-migration/how-tos/fix-import-errors)
* [Referenční příručka mapování polí](/l/cs/user-guide/data-migration/capabilities/field-mapping)
* [Omezení jedinečnosti](/l/cs/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: Relační pole
description: Connect records across different objects using relation fields.
description: Propojte záznamy napříč různými objekty pomocí relačních polí.
---
## Types of Relations
## Typy relací
### One-to-Many
### Jeden k mnoha
One record in Object A can be linked to many records in Object B.
Jeden záznam v Objektu A může být propojen s mnoha záznamy v Objektu B.
**Example:** One Company can have many People (employees).
**Příklad:** Jedna společnost může mít mnoho lidí (zaměstnanců).
### Many-to-One
### Mnoho k jednomu
Many records in Object A can be linked to one record in Object B.
Mnoho záznamů v Objektu A může být propojeno s jedním záznamem v Objektu B.
**Example:** Many People can belong to one Company.
**Příklad:** Mnoho lidí může patřit k jedné společnosti.
### Relations to Multiple Object Types
### Relace k více typům objektů
Some objects can link to multiple object types on one side of the relation.
Některé objekty mohou na jedné straně relace odkazovat na více typů objektů.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**Příklad:** Poznámku lze současně připojit k jedné osobě, jedné společnosti a jedné příležitosti. Poznámka je na straně "mnoho" a propojuje se s více stranami "jeden".
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
Podobně může Projekt (na straně "jeden") přijímat odkazy od více lidí, více společností a více poznámek.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**Omezení importu/exportu**: Relace směřující k více typům objektů zatím nejsou podporovány pro import/export CSV. Je to v našem plánu.
</Warning>
### Many-to-Many
### Mnoho k mnoha
Many records in Object A can be linked to many records in Object B.
Mnoho záznamů v Objektu A může být propojeno s mnoha záznamy v Objektu B.
**Example:** Many People can be linked to many Projects, and vice versa.
**Příklad:** Mnoho lidí může být propojeno s mnoha projekty a naopak.
<Warning>
**Many-to-Many is not yet supported.**
**Mnoho k mnoha zatím není podporováno.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
Tento typ relace je plánován na 1. pololetí 2026. Jako dočasné řešení vytvořte prostřední "spojovací" objekt (např. "Project Assignments"), který má relace typu mnoho k jednomu k oběma objektům.
</Warning>
## Creating a Relation Field
## Vytvoření relačního pole
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. Přejděte do **Nastavení → Datový model**
2. Vyberte objekt, do kterého chcete přidat relaci
3. Klikněte na **+ Add Field**
4. Vyberte **Relation** jako typ pole
5. Vyberte cílový objekt nebo objekty, ke kterým chcete relaci vytvořit
6. Nakonfigurujte nastavení relace:
* **Název pole na zdrojovém objektu**: Název relačního pole na objektu, který upravujete
* **Název pole na cílovém objektu**: Název relačního pole, které se zobrazí na cílovém objektu
* Typ vztahu (jeden k mnoha, mnoho k jednomu)
7. Klikněte na **Uložit**
## Standard Relations
## Standardní relace
Twenty comes with pre-built relations between standard objects:
Twenty obsahuje předpřipravené relace mezi standardními objekty:
| From Object | To Object | Relation Type |
| ------------ | ----------- | ------------- |
| Osoby | Společnosti | Many-to-One |
| Příležitosti | Společnosti | Many-to-One |
| Příležitosti | Osoby | Many-to-One |
| Z objektu | Do objektu | Typ vztahu |
| ------------ | ----------- | --------------- |
| Osoby | Společnosti | Mnoho k jednomu |
| Příležitosti | Společnosti | Mnoho k jednomu |
| Příležitosti | Osoby | Mnoho k jednomu |
## Osvědčené postupy
### Planning Relations
### Plánování relací
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **Zmapujte svůj datový model**: Relace si naplánujte před jejich vytvořením
* **Zvažte směr**: Promyslete, který objekt "vlastní" relaci
* **Vyhněte se cyklickým závislostem**: Udržujte svůj datový model čistý
### Naming Relations
### Pojmenovávání relací
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **Používejte jasné názvy**: Ať je zřejmé, co relace představuje
* **Buďte konzistent**: Používejte podobné vzory pojmenování napříč relacemi
* **Zvažte obě strany**: Vhodně pojmenujte obě strany relace
### Performance
### Výkon
* **Don't over-relate**: Too many relations can slow down your workspace
* **Nepřehánějte to s relacemi**: Příliš mnoho relací může zpomalit váš pracovní prostor
## Limitations
## Omezení
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **Mazání relací** odstraní propojení, nikoli související záznamy
* **Cyklickým relacím** je třeba se kvůli integritě dat vyhnout
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: Vytváření vlastních polí
description: Návod krok za krokem k přidání vlastních polí k libovolnému objektu.
---
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
Vlastní pole vám umožňují zachytit informace specifické pro vaše podnikání. Přidejte je k libovolnému objektu—standardnímu i vlastnímu.
## Steps
## Postup
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/cs/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. Přejděte do **Nastavení → Datový model**
2. Vyberte objekt, ke kterému chcete přidat pole
3. Klikněte na **+ Add Field**
4. Zvolte **typ pole** (všechny typy viz [Pole](/l/cs/user-guide/data-model/capabilities/fields))
5. Zadejte **název pole** a volitelný popis
6. Nakonfigurujte nastavení specifická pro dané pole (viz níže)
7. Klikněte na **Uložit**
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**Rychlá metoda:** Klikněte na **+** na konci záhlaví sloupců v libovolném tabulkovém zobrazení → **Customize fields**.
## Show the Field in Views
## Zobrazit pole v zobrazeních
New fields aren't automatically visible. To display:
Nová pole nejsou automaticky viditelná. Pro zobrazení:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. Otevřete tabulkové zobrazení objektu
2. Klikněte na **Možnosti → Pole**
3. Klikněte na **ikonu oka** vedle svého pole a zobrazte jej
4. Přetažením změňte pořadí
## Configuration Options
## Možnosti konfigurace
### For Select / Multi-Select
### Pro výběr / vícevýběr
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. Klikněte na **+ Add option** a vytvořte možnosti
2. Nastavte **výchozí možnost**, pokud chcete
3. Přetažením změňte pořadí možností
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/cs/user-guide/data-migration/capabilities/field-mapping).
**Pro importy používejte názvy API.** V Nastavení povolte **Advanced mode**, abyste viděli názvy API. Viz [Mapování polí](/l/cs/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### Pro měnová pole
Set the **default currency** (USD, EUR, etc.) for new records.
Nastavte **výchozí měnu** (USD, EUR apod.) pro nové záznamy.
### For Phone Fields
### Pro telefonní pole
Set the **default country code** to pre-fill for new phone numbers.
Nastavte **výchozí kód země**, který se předvyplní pro nová telefonní čísla.
### Making a Field Unique
### Nastavení jedinečnosti pole
Toggle **Unique** to prevent duplicate values across records.
Přepněte **Unique**, abyste zabránili duplicitním hodnotám napříč záznamy.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
Pokud existují duplicity (včetně těch ve smazaných záznamech), zobrazí se chyba. Nejprve odstraňte duplicity.
</Note>
### Setting Default Values
### Nastavení výchozích hodnot
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
U výběrových polí můžete zvolit, která možnost bude u nových záznamů předvybraná. U zaškrtávacích polí nastavte, zda bude ve výchozím stavu zaškrtnuto, nebo ne.
## Deactivating a Field
## Deaktivace pole
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. Přejděte do **Nastavení → Datový model**
2. Najděte pole
3. Klikněte na **⋮ → Deactivate**
Data is preserved. You can reactivate or permanently delete later.
Data jsou zachována. Později můžete znovu aktivovat nebo trvale smazat.
## Related
## Související
* [Fields](/l/cs/user-guide/data-model/capabilities/fields) — all field types explained
* [Data Model FAQ](/l/cs/user-guide/data-model/how-tos/data-model-faq) — common questions
* [Pole](/l/cs/user-guide/data-model/capabilities/fields) — vysvětlení všech typů polí
* [FAQ k datovému modelu](/l/cs/user-guide/data-model/how-tos/data-model-faq) — časté otázky
@@ -1,6 +1,6 @@
---
title: Datový model
description: Learn what a data model is and how to design one that fits your business.
description: Zjistěte, co je datový model a jak navrhnout takový, který bude vyhovovat vašemu podnikání.
image: /images/user-guide/fields/custom_data_model.png
---
@@ -8,120 +8,120 @@ image: /images/user-guide/fields/custom_data_model.png
<img src="/images/user-guide/fields/custom_data_model.png" alt="Datový model" />
</Frame>
## What is a Data Model?
## Co je to datový model?
Datový model je struktura, která definuje, jak jsou informace organizovány ve vašem CRM. Think of it as the **blueprint** of your customer datayou design it once, then fill it with your actual data.
Datový model je struktura, která definuje, jak jsou informace organizovány ve vašem CRM. Představte si ho jako **plán** vašich zákaznických dat — navrhnete ho jednou a potom ho naplníte skutečnými daty.
## Key Concepts
## Klíčové pojmy
### Objekty
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
**Objekty** jsou hlavní kategorie dat ve vašem CRM. Každý objekt představuje typ entity, kterou chcete sledovat.
Twenty comes with standard objects:
Twenty obsahuje standard objekty:
* **People** — individuals (contacts, leads, partners)
* **Companies** — organizations
* **Opportunities** — deals or sales
* **Notes** — attached notes on records
* **Tasks** — to-dos linked to records
* **Lidé** — jednotlivci (kontakty, leady, partneři)
* **Společnosti** — organizace
* **Příležitosti** — obchodní případy či prodeje
* **Poznámky** — připojené poznámky u záznamů
* **Úkoly** — úkoly propojené se záznamy
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
Můžete také vytvářet **vlastní objekty** pro cokoli specifického pro vaše podnikání (např. Projekty, Předplatná, Události).
### Pole
**Fields** are the properties or attributes that describe each object. They store the actual information.
**Pole** jsou vlastnosti nebo atributy, které popisují každý objekt. Ukládají skutečné informace.
For example, the **People** object has fields like:
Například objekt **Lidé** má pole jako:
* Název
* Email
* Telefon
* Pracovní pozice
* Company (a relation to the Companies object)
* Společnost (vztah k objektu Společnosti)
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
Pole mají různé **typy**: text, číslo, datum, výběr, vícenásobný výběr, vztah a další. Do libovolného objektu můžete přidat vlastní pole.
### Záznamy
**Records** are the individual entries within an object — the actual data you create and manage.
**Záznamy** jsou jednotlivé položky v rámci objektu — skutečná data, která vytváříte a spravujete.
Například:
* "John Smith" is a **record** in the People object
* "Acme Corp" is a **record** in the Companies object
* "John Smith" je **záznam** v objektu Lidé
* "Acme Corp" je **záznam** v objektu Společnosti
**An analogy:**
**Přirovnání:**
| Data Model Concept | Real-World Analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **Polí** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
| Koncept datového modelu | Přirovnání z reálného světa |
| ----------------------- | ---------------------------------- |
| **Objekty** | Oddíly v knize (kategorie) |
| **Polí** | Sloupce v tabulce (vlastnosti) |
| **Záznamy** | Řádky v tabulce (skutečné položky) |
You design the data model (objects + fields) once, then create many records within that structure.
Datový model (objekty + pole) navrhnete jednou a poté v této struktuře vytváříte mnoho záznamů.
## Why Customize Your Data Model?
## Proč přizpůsobit svůj datový model?
Každý podnik funguje jinak. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
Každý podnik funguje jinak. Přizpůsobením datového modelu můžete přetvořit Twenty podle **svých** procesů namísto toho, abyste své procesy nutili do rigidního systému.
Twenty offers full flexibility:
Twenty nabízí plnou flexibilitu:
* Create as many custom objects as you need
* Add unlimited custom fields
* The price doesn't change based on customization
* Vytvořte tolik vlastních objektů, kolik potřebujete
* Přidejte neomezený počet vlastních polí
* Cena se podle rozsahu přizpůsobení nemění
## Tips to Design Your Data Model
## Tipy pro návrh vašeho datového modelu
### 1. Start with Your Core Objects
### 1. Začněte se svými klíčovými objekty
Identify the main concepts you work with. Twenty already provides:
Identifikujte hlavní pojmy, se kterými pracujete. Twenty již poskytuje:
* **People** — your contacts
* **Companies** — your accounts
* **Opportunities** — your deals
* **Lidé** — vaše kontakty
* **Společnosti** — vaše účty
* **Příležitosti** — vaše obchodní případy
Think about what else you might need:
Zamyslete se, co dalšího můžete potřebovat:
* Stripe would need a `Subscriptions` object
* Airbnb would need a `Trips` object
* An accelerator would need a `Batches` object
* Stripe by potřeboval objekt `Subscriptions`
* Airbnb by potřebovalo objekt `Trips`
* Akcelerátor by potřeboval objekt `Batches`
### 2. Use Fields for Variations, Not New Objects
### 2. Na varianty používejte pole, ne nové objekty
If something is just a characteristic of an existing object, make it a **field**.
Pokud je něco jen charakteristikou existujícího objektu, udělejte z toho **pole**.
**Use fields for:**
**Pole používejte pro:**
* Categories and labels (e.g., `Industry` for Companies)
* Status values (e.g., `Stage` for Opportunities)
* Attributes and properties
* Kategorie a štítky (např. `Industry` u Společností)
* Stavové hodnoty (např. `Stage` u Příležitostí)
* Atributy a vlastnosti
### 3. Create an Object When It Stands on Its Own
### 3. Vytvořte objekt, když stojí samostatně
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
Pokud má koncept svůj vlastní životní cyklus, vlastnosti nebo vztahy, zaslouží si objekt.
**Create an object for:**
**Vytvořte objekt pro:**
* **Projects** — have deadlines, owners, and tasks
* **Subscriptions** — connect companies, products, and invoices
* **Events** — involve attendees and follow-up actions
* **Projekty** — mají termíny, vlastníky a úkoly
* **Předplatná** — propojují společnosti, produkty a faktury
* **Události** — zahrnují účastníky a následné akce
Tyto jdou nad rámec jednoho pole, protože nesou svá vlastní data a vztahy.
### 4. Create an Object When Records Are Open-Ended
### 4. Vytvořte objekt, když je počet záznamů neurčený
If something can be linked multiple times and you don't know how many, use an object.
Pokud lze něco propojit vícekrát a nevíte, kolikrát, použijte objekt.
**Bad approach:**
Creating fields like `Product 1`, `Product 2`, `Product 3`...
**Špatný přístup:**
Vytváření polí jako `Product 1`, `Product 2`, `Product 3`...
**Good approach:**
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
**Správný přístup:**
Vytvořte objekt `Products` a propojte ho se záznamy. Tímto způsobem podpoříte jeden, dva nebo sto produktů, aniž byste měnili svůj model.
### 5. Keep It Simple First
### 5. Začněte jednoduše
Start with fields. Move to new objects only when you feel the limits:
Začněte s poli. Přejděte na nové objekty teprve, když narazíte na limity:
* Too many fields on one object
* Repeated records that should be separate
@@ -3,7 +3,7 @@ title: Nastavení pracovního prostoru
description: Přizpůsobte název a styl pracovního prostoru.
---
Those are accessible under **Settings → General**.
Najdete je v části **Nastavení → Obecné**.
## Obrázek pracovního prostoru
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: Pole a sloupce
description: Vyberte, která pole zobrazit a jak je uspořádat.
---
## Selecting Fields to Display
## Výběr polí k zobrazení
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
Každé zobrazení může zobrazovat jinou sadu polí. Přizpůsobte, co je viditelné, abyste se zaměřili na důležité informace.
### Show or Hide Fields
### Zobrazit nebo skrýt pole
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. Klikněte na **Možnosti** vpravo nahoře
2. Klikněte na **Pole**
3. Klikněte na **ikonu oka** vedle každého pole pro jeho zobrazení/skrytí
### Reorder Fields
### Změna pořadí polí
Change the order fields appear in your view:
Změňte pořadí, ve kterém se pole zobrazují ve zobrazení:
1. Click **Options → Fields**
2. Drag fields up or down
3. Changes save automatically
1. Klikněte na **Možnosti → Pole**
2. Přetáhněte pole nahoru nebo do
3. Změny se ukládají automaticky
## Field Display by View Type
## Zobrazení polí podle typu zobrazení
### Zobrazení tabulky
* Fields appear as columns
* Resize columns by dragging borders
* Pole se zobrazují jako sloupce
* Změňte velikost sloupců tažením okrajů
### Zobrazení Kanban
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* Pole se zobrazují na kartách
* Změňte pořadí přes Možnosti → Pole
* Použijte kompaktní zobrazení pro skrytí všech polí
### Calendar Views
### Kalendářní zobrazení
* Selected fields show on calendar events
* Configure via Options → Fields
* Vybraná pole se zobrazují u událostí v kalendáři
* Nastavte přes Možnosti → Pole
## Osvědčené postupy
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **Zobrazujte jen to, co je potřeba** — příliš mnoho polí zahlcuje zobrazení
* **Umístěte důležitá pole na začátek** — nejpoužívanější sloupce vlevo
* **Vytvářejte více zobrazení** — různé sady polí pro různé účely
* **Využijte viditelnost polí na úrovni zobrazení** — stejný objekt, jiné zaměření
## Related
## Související
* [Table Views](/l/cs/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/cs/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [Tabulková zobrazení](/l/cs/user-guide/views-pipelines/capabilities/table-views) — funkce seznamového zobrazení
* [Kanban zobrazení](/l/cs/user-guide/views-pipelines/capabilities/kanban-views) — zobrazení založená na kartách
@@ -1,6 +1,6 @@
---
title: Kanban Board Views
description: Learn how to use Kanban views to visualize and manage your workflows.
title: Zobrazení kanbanové tabule
description: Zjistěte, jak používat kanbanová zobrazení k vizualizaci a správě svých pracovních postupů.
image: /images/user-guide/kanban-views/kanban.png
---
@@ -14,9 +14,9 @@ Kanban zobrazení vizuálně zobrazují tok procesů, kde každý sloupec předs
Každou kartu můžete přesouvat mezi fázemi, jak prochází vaším pracovní postupem, tažením a pouštěním. Pro pokračování podržte klik na kartě a přesuňte ji do další fáze.
<VimeoEmbed videoId="927888627" title="Video demonstration" />
<VimeoEmbed videoId="927888627" title="Ukázka videa" />
## Add and Delete Stages
## Přidání a odstranění fází
Workflow si můžete přizpůsobit tak, aby vyhovoval vašim potřebám, pomocí fází, které představují hodnotu ve výběrovém poli:
@@ -24,15 +24,15 @@ Workflow si můžete přizpůsobit tak, aby vyhovoval vašim potřebám, pomocí
Pro přidání fáze přejděte do nastavení výběrového pole tak, že přejdete na Nastavení > Datový model, zvolíte svůj objekt a poté pole, na němž vaše Kanban tabule závisí.
<VimeoEmbed videoId="927890428" title="Video demonstration" />
<VimeoEmbed videoId="927890428" title="Ukázka videa" />
### Odstranit fáze
To remove a stage, hover the stage name or the `⋮` icon, click `Edit from settings` in the Select field settings, and then click **Delete** next to the relevant stage.
Chcete-li odstranit fázi, najeďte na název fáze nebo na ikonu `⋮`, klikněte na `Upravit ze nastavení` v nastavení pole výběru a poté klikněte na **Odstranit** vedle příslušné fáze.
## Display Fields
## Zobrazená pole
Svou Kanban tabuli můžete nakonfigurovat tak, aby zobrazovala některá pole a skrývala jiná. To hide a field, click on **Options** on the top right, then on **Fields** to bring up the list of options. Look for the field needed in the Hidden Fields section and click on the eye button to display the field.
Svou Kanban tabuli můžete nakonfigurovat tak, aby zobrazovala některá pole a skrývala jiná. Chcete-li skrýt pole, klikněte vpravo nahoře na **Možnosti** a poté na **Pole**, abyste zobrazili seznam možností. Vyhledejte požadované pole v sekci Skrytá pole a klikněte na tlačítko s ikonou oka, abyste pole zobrazili.
Pole můžete také přeuspořádat tak, že podržíte název pole a přetáhnete ho tam, kam chcete.
@@ -40,60 +40,60 @@ Pole můžete také přeuspořádat tak, že podržíte název pole a přetáhne
## Kompaktní zobrazení
You can hide all the fields and get an overview of all records at a glance. To enable:
Můžete skrýt všechna pole a získat přehled o všech záznamech na první pohled. Chcete-li zapnout:
1. Click **Options** on the top right
2. Turn on the toggle for **Compact view**
1. Klikněte vpravo nahoře na **Možnosti**
2. Zapněte přepínač **Kompaktní zobrazení**
<img src="/images/user-guide/kanban-views/compact-view.png" style={{width:'100%'}} />
## Column Aggregations
## Agregace sloupců
Each column in a Kanban view can display aggregated values at the top, helping you understand your data at a glance.
Každý sloupec v kanbanovém zobrazení může nahoře zobrazovat agregované hodnoty, což pomáhá rychle pochopit vaše data.
### Available Aggregations
### Dostupné agregace
| Aggregation | Popis |
| ----------- | --------------------------------------------- |
| **Count** | Number of records in the column |
| **Sum** | Total of a numeric field (e.g., deal amounts) |
| **Average** | Average value of a numeric field |
| **Min** | Lowest value |
| **Max** | Highest value |
| Agregace | Popis |
| ----------- | -------------------------------------------- |
| **Počet** | Počet záznamů ve sloupci |
| **Součet** | Součet číselného pole (např. částky obchodů) |
| **Průměr** | Průměrná hodnota číselného pole |
| **Minimum** | Nejnižší hodnota |
| **Maximum** | Nejvyšší hodnota |
### Configuring Aggregations
### Nastavení agregací
1. Click on the number displayed next to the Stage value, at the top of a column
2. Select the aggregation type
3. Choose the field to aggregate
1. Klikněte na číslo zobrazené vedle hodnoty fáze v horní části sloupce
2. Vyberte typ agregace
3. Zvolte pole pro agregaci
**Example:** Show total deal value per stage by aggregating the Amount field with Sum.
**Příklad:** Zobrazte celkovou hodnotu obchodu podle fází agregováním pole Částka pomocí součtu.
## When to Use Kanban Views
## Kdy používat kanbanová zobrazení
Kanban views are ideal for:
Kanbanová zobrazení jsou ideální pro:
* **Sales pipelines**: Track deals through stages from lead to close
* **Project management**: Monitor tasks through workflow states
* **Recruitment**: Track candidates through hiring stages
* **Any staged process**: Visualize any workflow with defined stages
* **Prodejní pipeline**: Sledujte obchody napříč fázemi od leadu po uzavření
* **Projektové řízení**: Sledujte úkoly napříč stavy pracovního postupu
* **Nábor**: Sledujte kandidáty jednotlivými fázemi náboru
* **Jakýkoli vícestupňový proces**: Vizualizujte jakýkoli pracovní postup s definovanými fázemi
## Osvědčené postupy
### Organize Your Stages
### Uspořádejte své fáze
* **Limit stages**: 5-7 stages is ideal for visibility
* **Clear naming**: Use descriptive stage names
* **Logical order**: Arrange stages in process order
* **Omezte počet fází**: 5-7 fází je ideálních pro přehlednost
* **Jasné pojmenování**: Používejte popisné názvy fází
* **Logické pořadí**: Uspořádejte fáze podle pořadí procesu
### Optimize Card Display
### Optimalizujte zobrazení karet
* **Show key fields**: Display only the most important information
* **Use compact view**: For high-level overviews
* **Color coding**: Use stage colors to quickly identify status
* **Zobrazujte klíčová pole**: Zobrazte pouze nejdůležitější informace
* **Používejte kompaktní zobrazení**: Pro stručné přehledy
* **Barevné rozlišení**: Používejte barvy fází pro rychlou identifikaci stavu
### Maintain Data Quality
### Udržujte kvalitu dat
* **Update regularly**: Keep cards moving through stages
* **Archive completed**: Move closed items out of active view
* **Review stale cards**: Follow up on cards stuck in stages
* **Pravidelně aktualizujte**: Udržujte karty v pohybu napříč fázemi
* **Archivujte dokončené**: Přesuňte uzavřené položky mimo aktivní zobrazení
* **Projděte zastaralé karty**: Řešte karty, které ustrnuly ve fázích
@@ -1,140 +1,140 @@
---
title: Closed Won Automations
description: Automate post-win activities when opportunities close.
title: Automatizace pro Uzavřeno - Vyhráno
description: Automatizujte činnosti po výhře, když se příležitosti uzavřou.
---
When a deal closes, multiple things need to happen: update company status, notify team members, create onboarding tasks. Automate all of this with a single workflow.
Když se obchod uzavře, je potřeba provést několik věcí: aktualizovat stav společnosti, upozornit členy týmu, vytvořit onboardingové úkoly. Automatizujte to vše jediným pracovním postupem.
## The Problem
## Problém
When an opportunity moves to "Closed Won":
Když se příležitost přesune do stavu "Uzavřeno - Vyhráno":
* Company type needs to change from "Prospect" to "Customer"
* Onboarding tasks need to be created
* Customer success team needs to be notified
* Sales rep needs confirmation
* Typ společnosti je třeba změnit z "Zájemce" na "Zákazník"
* Je třeba vytvořit onboardingové úkoly
* Je třeba upozornit tým Customer Success
* Obchodní zástupce potřebuje potvrzení
Doing this manually is time-consuming and error-prone.
Dělat to ručně je časově náročné a náchylné k chybám.
## The Solution
## Řešení
Create a workflow that handles all post-win activities automatically.
Vytvořte pracovní postup, který automaticky zajistí všechny činnosti po výhře.
## Complete Workflow Setup
## Kompletní nastavení pracovního postupu
### Step 1: Create the Workflow
### Krok 1: Vytvořte pracovní postup
1. Go to **Settings → Workflows**
2. Click **+ New Workflow**
3. Name it "Deal Won - Post-Win Automation"
1. Přejděte na **Nastavení → Pracovní postupy**
2. Klikněte na **+ Nový pracovní postup**
3. Pojmenujte jej "Vyhraný obchod - Automatizace po výhře"
### Step 2: Configure the Trigger
### Krok 2: Nakonfigurujte spouštěč
1. Select **Record is Updated**
2. Choose **Opportunities**
3. Under "Fields to monitor", select **Stage**
1. Vyberte **Záznam je aktualizován**
2. Vyberte **Příležitosti**
3. V části "Pole ke sledování" vyberte **Stádium**
### Step 3: Add Stage Filter
### Krok 3: Přidejte filtr podle stádia
1. Add **Filter** action
2. Condition: `{{trigger.object.stage}}` equals "Closed Won"
1. Přidejte akci **Filtr**
2. Podmínka: `{{trigger.object.stage}}` se rovná "Uzavřeno - Vyhráno"
### Step 4: Update Company Type
### Krok 4: Aktualizujte typ společnosti
1. Add **Update Record** action
1. Přidejte akci **Aktualizovat záznam**
2. Nakonfigurujte:
| Pole | Hodnota |
| ------------------- | ------------------------------- |
| **Object** | Společnosti |
| **Record** | `{{trigger.object.company.id}}` |
| **Typ** | Zákazník |
| **First Deal Date** | `{{trigger.object.closedAt}}` |
| **Vlastník účtu** | `{{trigger.object.owner.id}}` |
| Pole | Hodnota |
| ------------------------- | ------------------------------- |
| **Objekt** | Společnosti |
| **Záznam** | `{{trigger.object.company.id}}` |
| **Typ** | Zákazník |
| **Datum prvního obchodu** | `{{trigger.object.closedAt}}` |
| **Vlastník účtu** | `{{trigger.object.owner.id}}` |
### Step 5: Create Onboarding Task
### Krok 5: Vytvořte onboardingový úkol
1. Add **Create Record** action
1. Přidejte akci **Vytvořit záznam**
2. Nakonfigurujte:
| Pole | Hodnota |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Object** | Úkoly |
| **Title** | `Onboarding: {{trigger.object.name}}` |
| **Assignee** | Customer Success team member |
| **Due Date** | 3 days from now |
| **Priority** | High |
| **Related Company** | `{{trigger.object.company.id}}` |
| **Related Opportunity** | `{{trigger.object.id}}` |
| **Description** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
| Pole | Hodnota |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| **Objekt** | Úkoly |
| **Název** | `Onboarding: {{trigger.object.name}}` |
| **Přiřazený** | Člen týmu Customer Success |
| **Datum splatnosti** | Za 3 dny |
| **Priorita** | Vysoká |
| **Související společnost** | `{{trigger.object.company.id}}` |
| **Související příležitost** | `{{trigger.object.id}}` |
| **Popis** | `Onboarding nového zákazníka pro {{trigger.object.company.name}}. Hodnota obchodu: {{trigger.object.amount}}` |
### Step 6: Notify Customer Success
### Krok 6: Upozorněte Customer Success
1. Add **Send Email** action
1. Přidejte akci **Odeslat e-mail**
2. Nakonfigurujte:
| Pole | Hodnota |
| ----------- | -------------------------------------------------- |
| **To** | customer-success@yourcompany.com |
| **Subject** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Body** | See example below |
| Pole | Hodnota |
| --------------- | --------------------------------------------------- |
| **Komu** | customer-success@yourcompany.com |
| **Předmět** | `🎉 Nový zákazník: {{trigger.object.company.name}}` |
| **Text zprávy** | Viz příklad níže |
**Email body example**:
**Příklad textu e-mailu**:
```
Hi CS Team,
Ahoj týme CS,
We have a new customer!
Máme nového zákazníka!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
Společnost: {{trigger.object.company.name}}
Obchod: {{trigger.object.name}}
Hodnota: {{trigger.object.amount}}
Obchodní zástupce: {{trigger.object.owner.name}}
Datum uzavření: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Onboardingový úkol byl automaticky vytvořen.
Let's give them a great start!
Dejme jim skvělý start!
```
### Step 7: Confirm to Sales Rep
### Krok 7: Potvrďte obchodnímu zástupci
1. Add another **Send Email** action
1. Přidejte další akci **Odeslat e-mail**
2. Nakonfigurujte:
| Pole | Hodnota |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| **To** | `{{trigger.object.owner.email}}` |
| **Subject** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Body** | Congratulations! Your deal has been processed. The customer success team has been notified and onboarding has begun. |
| Pole | Hodnota |
| --------------- | --------------------------------------------------------------------------------------------------- |
| **Komu** | `{{trigger.object.owner.email}}` |
| **Předmět** | `✅ Obchod uzavřen: {{trigger.object.name}}` |
| **Text zprávy** | Gratulujeme! Váš obchod byl zpracován. Tým Customer Success byl upozorněn a onboarding byl zahájen. |
### Step 8: Test and Activate
### Krok 8: Otestujte a aktivujte
1. Test by moving a test opportunity to "Closed Won"
1. Otestujte přesunutím testovací příležitosti do stavu "Uzavřeno - Vyhráno"
2. Ověřit:
* Company type changed to "Customer"
* Onboarding task created
* CS team received email
* Sales rep received confirmation
3. Activate when ready
* Typ společnosti změněn na "Zákazník"
* Onboardingový úkol vytvořen
* Tým Customer Success obdržel e-mail
* Obchodní zástupce obdržel potvrzení
3. Až budete připraveni, aktivujte
## Handling Closed Lost
## Zpracování Uzavřeno - Prohráno
Create a similar workflow for lost deals:
Vytvořte podobný pracovní postup pro prohrané obchody:
### Trigger
### Spouštěč
* Record is Updated (Opportunities, Stage = "Closed Lost")
* Záznam je aktualizován (Příležitosti, Stádium = "Uzavřeno - Prohráno")
### Akce
1. **Create Record**: Task for "Lost Deal Analysis"
2. **Update Record**: Add lost reason to company record
3. **Send Email**: Notify manager of lost deal
1. **Vytvořit záznam**: Úkol pro "Analýzu prohraného obchodu"
2. **Aktualizovat záznam**: Přidat důvod prohry do záznamu společnosti
3. **Odeslat e-mail**: Upozornit manažera na prohraný obchod
## Advanced: Multi-Step Onboarding
## Pokročilé: Vícefázový onboarding
For complex onboarding, create multiple tasks:
Pro složitější onboarding vytvořte více úkolů:
```javascript
export const main = async (params) => {
@@ -149,31 +149,31 @@ export const main = async (params) => {
};
```
Use **Iterator** to create each task from the array.
Použijte **Iterator** k vytvoření každého úkolu z pole.
## Customization Ideas
## Nápady na přizpůsobení
### Keep your other tools up-to-date
### Udržujte své další nástroje aktuální
* Create customer in billing system with an **HTTP Request**
* Vytvořte zákazníka ve fakturačním systému pomocí **HTTP Request**
### Conditional Actions
### Podmíněné akce
Use **Filter** actions to:
Použijte akce **Filtr** k:
* Different onboarding for enterprise vs SMB
* Different assignees based on region
* Skip notifications for small deals
* Odlišný onboarding pro enterprise vs SMB
* Různé přiřazené osoby podle regionu
* Přeskočit upozornění u malých obchodů
### Include Deal Details
### Zahrnout podrobnosti obchodu
Use **Code** action to format:
Použijte akci **Code** k formátování:
* Deal summary documents
* Handoff notes for CS team
* Custom onboarding checklists
* Dokumenty se souhrnem obchodu
* Poznámky k předání pro tým Customer Success
* Vlastní onboardingové kontrolní seznamy
## Related
## Související
* [Workflow Actions](/l/cs/user-guide/workflows/capabilities/workflow-actions)
* [Send Emails from Workflows](/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Akce pracovních postupů](/l/cs/user-guide/workflows/capabilities/workflow-actions)
* [Odesílání e-mailů z pracovních postupů](/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows)
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Authentifizierung
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Zweck |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Notizen:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: KI-FAQ
description: Häufig gestellte Fragen zu KI-Funktionen in Twenty.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="Wann werden KI-Funktionen verfügbar sein?">
KI-Funktionen befinden sich derzeit in Entwicklung und werden bald als Beta veröffentlicht. Bleiben Sie dran Updates folgen!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="Welche KI-Funktionen sind geplant?">
Wir entwickeln zwei zentrale KI-Funktionen:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **KI-Chatbot**: Ein kontextbewusster Assistent, der auf Ihre Twenty-Daten zugreifen kann und Sie bei Anfragen unterstützt
2. **KI-Agenten in Workflows**: Intelligente Automatisierung, die Daten verarbeiten, Entscheidungen treffen und Aufgaben innerhalb Ihrer Workflows ausführen kann
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="Haben KI-Agenten Zugriff auf alle meine Daten?">
KI-Agenten werden über das Berechtigungssystem verwaltet. Sie können KI-Agenten unter **Einstellungen → Rollen** bestimmte Rollen zuweisen und haben damit volle Kontrolle darüber, auf welche Daten sie zugreifen können und welche Aktionen sie ausführen dürfen.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="Wie funktionieren KI-Credits?">
KI-Aktionen verbrauchen Workflow-Credits basierend auf der Komplexität der Aufgabe und dem verwendeten KI-Modell. Weitere Details werden verfügbar sein, wenn die Funktionen veröffentlicht werden.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="Kann ich meine eigenen KI-Modelle verwenden?">
Zunächst wird Twenty integrierte KI-Modelle verwenden. Unterstützung für benutzerdefinierte oder externe KI-Modelle kann in zukünftigen Versionen basierend auf Nutzerfeedback hinzugefügt werden.
</Accordion>
</AccordionGroup>
@@ -180,22 +180,22 @@ Details finden Sie unter [So aktualisieren Sie vorhandene Datensätze](/l/de/use
</Accordion>
<Accordion title="Kann ich Unternehmen ohne verknüpfte Personen importieren?">
Ja! You can import companies first, then import People later and link them using the company domain.
Ja! Sie können zunächst Unternehmen importieren und später Personen importieren und sie mithilfe der Unternehmensdomain verknüpfen.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="Was passiert, wenn ich eine bereits vorhandene Domain importiere?">
Wenn Sie einen eindeutigen Bezeichner (Domain oder id) angeben, der mit einem vorhandenen Unternehmen übereinstimmt, aktualisiert Twenty dieses Unternehmen, statt ein Duplikat zu erstellen.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="Wie behebe ich Fehler vom Typ 'doppelte Domain'?">
Entfernen Sie entweder das Duplikat aus Ihrer Datei oder fügen Sie die `id` des Unternehmens hinzu, um stattdessen den vorhandenen Datensatz zu aktualisieren.
</Accordion>
</AccordionGroup>
## Fehlerbehebung
Having issues? Check:
Gibt es Probleme? Prüfen Sie:
* [How to Fix Import Errors](/l/de/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/de/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/de/user-guide/data-migration/capabilities/uniqueness-constraints)
* [So beheben Sie Importfehler](/l/de/user-guide/data-migration/how-tos/fix-import-errors)
* [Referenz zur Feldzuordnung](/l/de/user-guide/data-migration/capabilities/field-mapping)
* [Eindeutigkeitsbeschränkungen](/l/de/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: Relationsfelder
description: Connect records across different objects using relation fields.
description: Verbinden Sie Datensätze über verschiedene Objekte hinweg mithilfe von Beziehungsfeldern.
---
## Types of Relations
## Arten von Beziehungen
### One-to-Many
### Eins-zu-Viele
One record in Object A can be linked to many records in Object B.
Ein Datensatz in Objekt A kann mit vielen Datensätzen in Objekt B verknüpft werden.
**Example:** One Company can have many People (employees).
**Beispiel:** Ein Unternehmen kann viele Personen (Mitarbeitende) haben.
### Many-to-One
### Viele-zu-Eins
Many records in Object A can be linked to one record in Object B.
Viele Datensätze in Objekt A können mit einem Datensatz in Objekt B verknüpft werden.
**Example:** Many People can belong to one Company.
**Beispiel:** Viele Personen können zu einem Unternehmen gehören.
### Relations to Multiple Object Types
### Beziehungen zu mehreren Objekttypen
Some objects can link to multiple object types on one side of the relation.
Einige Objekte können auf einer Seite der Beziehung mit mehreren Objekttypen verknüpft werden.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**Beispiel:** Eine Notiz kann gleichzeitig einer Person UND einem Unternehmen UND einer Opportunity zugeordnet werden. Die Notiz befindet sich auf der "Viele"-Seite und verbindet sich mit mehreren "Eins"-Seiten.
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
Ähnlich könnte ein Projekt (auf der "Eins"-Seite) Verknüpfungen von mehreren Personen, mehreren Unternehmen und mehreren Notizen erhalten.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**Einschränkung beim Import/Export**: Beziehungen, die auf mehrere Objekttypen zeigen, werden für den CSV-Import/-Export noch nicht unterstützt. Dies steht auf unserer Roadmap.
</Warning>
### Many-to-Many
### Viele-zu-Viele
Many records in Object A can be linked to many records in Object B.
Viele Datensätze in Objekt A können mit vielen Datensätzen in Objekt B verknüpft werden.
**Example:** Many People can be linked to many Projects, and vice versa.
**Beispiel:** Viele Personen können mit vielen Projekten verknüpft werden, und umgekehrt.
<Warning>
**Many-to-Many is not yet supported.**
**Viele-zu-Viele-Beziehungen werden noch nicht unterstützt.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
Dieser Beziehungstyp ist für H1 2026 geplant. Als Workaround erstellen Sie ein zwischengeschaltetes "Junction"-Objekt (z. B. "Projektzuweisungen"), das Viele-zu-Eins-Beziehungen zu beiden Objekten hat.
</Warning>
## Creating a Relation Field
## Beziehungsfeld erstellen
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. Gehen Sie zu **Einstellungen → Datenmodell**
2. Wählen Sie das Objekt aus, dem Sie die Beziehung hinzufügen möchten
3. Klicken Sie auf **+ Feld hinzufügen**
4. Wählen Sie **Relation** als Feldtyp
5. Wählen Sie die Zielobjekte aus, zu denen die Beziehung hergestellt werden soll
6. Konfigurieren Sie die Relationseinstellungen:
* **Feldname am Quellobjekt**: Der Name des Beziehungsfelds im Objekt, das Sie bearbeiten
* **Feldname am Zielobjekt**: Der Name des Beziehungsfelds, das im Zielobjekt angezeigt wird
* Beziehungstyp (Eins-zu-Viele, Viele-zu-Eins)
7. Klicken Sie auf **Speichern**
## Standard Relations
## Standardbeziehungen
Twenty comes with pre-built relations between standard objects:
Twenty enthält vordefinierte Beziehungen zwischen Standardobjekten:
| From Object | To Object | Relation Type |
| ------------- | ----------- | ------------- |
| Personen | Unternehmen | Many-to-One |
| Opportunities | Unternehmen | Many-to-One |
| Opportunities | Personen | Many-to-One |
| Ausgangsobjekt | Zielobjekt | Beziehungstyp |
| -------------- | ----------- | ------------- |
| Personen | Unternehmen | Viele-zu-Eins |
| Opportunities | Unternehmen | Viele-zu-Eins |
| Opportunities | Personen | Viele-zu-Eins |
## Beste Praktiken
### Planning Relations
### Beziehungen planen
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **Skizzieren Sie Ihr Datenmodell**: Planen Sie Beziehungen, bevor Sie sie erstellen
* **Berücksichtigen Sie die Richtung**: Überlegen Sie, welches Objekt die Beziehung "besitzt"
* **Vermeiden Sie zyklische Abhängigkeiten**: Halten Sie Ihr Datenmodell sauber
### Naming Relations
### Beziehungen benennen
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **Verwenden Sie klare Namen**: Machen Sie deutlich, was die Beziehung repräsentiert
* **Seien Sie konsistent**: Verwenden Sie ähnliche Benennungsschemata für alle Beziehungen
* **Berücksichtigen Sie beide Seiten**: Benennen Sie beide Seiten der Beziehung angemessen
### Performance
### Leistung
* **Don't over-relate**: Too many relations can slow down your workspace
* **Nicht übermäßig verknüpfen**: Zu viele Beziehungen können Ihren Arbeitsbereich verlangsamen
## Limitations
## Einschränkungen
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **Das Löschen von Beziehungen** entfernt die Verknüpfung, aber nicht die zugehörigen Datensätze
* **Zirkuläre Beziehungen** sollten zur Wahrung der Datenintegrität vermieden werden
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: Benutzerdefinierte Felder erstellen
description: Schritt-für-Schritt-Anleitung zum Hinzufügen benutzerdefinierter Felder zu jedem Objekt.
---
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
Benutzerdefinierte Felder ermöglichen es Ihnen, Informationen zu erfassen, die spezifisch für Ihr Unternehmen sind. Fügen Sie sie zu jedem Objekt hinzu ob Standard- oder benutzerdefiniert.
## Steps
## Schritte
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/de/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. Gehen Sie zu **Einstellungen → Datenmodell**
2. Wählen Sie das Objekt aus, dem Sie ein Feld hinzufügen möchten
3. Klicken Sie auf **+ Feld hinzufügen**
4. Wählen Sie einen **Feldtyp** (siehe [Felder](/l/de/user-guide/data-model/capabilities/fields) für alle Typen)
5. Geben Sie den **Feldnamen** und eine optionale Beschreibung ein
6. Konfigurieren Sie feldspezifische Einstellungen (siehe unten)
7. Klicken Sie auf **Speichern**
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**Schnellmethode:** Klicken Sie in einer beliebigen Tabellenansicht am Ende der Spaltenüberschriften auf **+** → **Felder anpassen**.
## Show the Field in Views
## Feld in Ansichten anzeigen
New fields aren't automatically visible. To display:
Neue Felder sind nicht automatisch sichtbar. Zum Anzeigen:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. Öffnen Sie die Tabellenansicht des Objekts
2. Klicken Sie auf **Optionen → Felder**
3. Klicken Sie auf das **Augensymbol** neben Ihrem Feld, um es anzuzeigen
4. Ziehen, um neu anzuordnen
## Configuration Options
## Konfigurationsoptionen
### For Select / Multi-Select
### Für Auswahl / Mehrfachauswahl
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. Klicken Sie auf **+ Option hinzufügen**, um Optionen zu erstellen
2. Legen Sie bei Bedarf eine **Standardoption** fest
3. Ziehen, um Optionen neu anzuordnen
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/de/user-guide/data-migration/capabilities/field-mapping).
**Verwenden Sie für Importe API-Namen.** Aktivieren Sie in den Einstellungen den **Erweiterten Modus**, um API-Namen anzuzeigen. Siehe [Feldzuordnung](/l/de/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### Für Währungsfelder
Set the **default currency** (USD, EUR, etc.) for new records.
Legen Sie die **Standardwährung** fest (USD, EUR usw.) für neue Datensätze.
### For Phone Fields
### Für Telefonfelder
Set the **default country code** to pre-fill for new phone numbers.
Legen Sie den **Standard-Ländercode** fest, der für neue Telefonnummern vorausgefüllt wird.
### Making a Field Unique
### Ein Feld eindeutig machen
Toggle **Unique** to prevent duplicate values across records.
Schalten Sie **Eindeutig** ein, um doppelte Werte über alle Datensätze hinweg zu verhindern.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
Wenn Duplikate vorhanden sind (auch in gelöschten Datensätzen), erhalten Sie eine Fehlermeldung. Bereinigen Sie Duplikate zuerst.
</Note>
### Setting Default Values
### Standardwerte festlegen
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
Für Auswahlfelder können Sie festlegen, welche Option bei neuen Datensätzen vorausgewählt ist. Für Kontrollkästchenfelder legen Sie fest, ob diese standardmäßig aktiviert oder deaktiviert sind.
## Deactivating a Field
## Ein Feld deaktivieren
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. Gehen Sie zu **Einstellungen → Datenmodell**
2. Suchen Sie das Feld
3. Klicken Sie auf **⋮ → Deaktivieren**
Data is preserved. You can reactivate or permanently delete later.
Daten bleiben erhalten. Sie können später reaktivieren oder dauerhaft löschen.
## Related
## Verwandt
* [Fields](/l/de/user-guide/data-model/capabilities/fields) — all field types explained
* [Data Model FAQ](/l/de/user-guide/data-model/how-tos/data-model-faq) — common questions
* [Felder](/l/de/user-guide/data-model/capabilities/fields) — alle Feldtypen erklärt
* [Datenmodell-FAQ](/l/de/user-guide/data-model/how-tos/data-model-faq) — häufige Fragen
@@ -1,11 +1,11 @@
---
title: Workspace Settings
title: Arbeitsbereichseinstellungen
description: Passen Sie den Namen und das Branding Ihres Arbeitsbereichs an.
---
Those are accessible under **Settings → General**.
Diese sind unter **Einstellungen → Allgemein** zu finden.
## Workspace Picture
## Arbeitsbereichsbild
* **Logo hochladen**: Ein benutzerdefiniertes Arbeitsbereichs-Logo hinzufügen
* **Unterstützte Formate**: PNG-, JPEG- und GIF-Dateien unter 10 MB
@@ -16,7 +16,7 @@ Those are accessible under **Settings → General**.
* **Name**: Anzeigenamen Ihres Arbeitsbereichs ändern
* Dieser Name erscheint für alle Mitglieder des Arbeitsbereichs
## Danger Zone
## Gefahrenzone
<Warning>
Das Löschen Ihres Arbeitsbereichs entfernt alle Daten dauerhaft und kann nicht rückgängig gemacht werden. Alle Arbeitsbereichsdaten werden für immer verloren gehen, alle Mitglieder verlieren sofort den Zugang, und diese Aktion kann nicht rückgängig gemacht werden.
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: Felder & Spalten
description: Wählen Sie aus, welche Felder angezeigt werden sollen und wie Sie sie organisieren.
---
## Selecting Fields to Display
## Anzuzeigende Felder auswählen
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
Jede Ansicht kann eine andere Auswahl an Feldern anzeigen. Passen Sie an, was sichtbar ist, um sich auf die wichtigen Informationen zu konzentrieren.
### Show or Hide Fields
### Felder anzeigen oder ausblenden
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. Klicken Sie oben rechts auf **Optionen**
2. Klicken Sie auf **Felder**
3. Klicken Sie auf das **Augensymbol** neben jedem Feld, um es ein- oder auszublenden
### Reorder Fields
### Felder neu anordnen
Change the order fields appear in your view:
Ändern Sie die Reihenfolge, in der Felder in Ihrer Ansicht angezeigt werden:
1. Click **Options → Fields**
2. Drag fields up or down
3. Changes save automatically
1. Klicken Sie auf **Optionen → Felder**
2. Ziehen Sie Felder nach oben oder unten
3. Änderungen werden automatisch gespeichert
## Field Display by View Type
## Feldanzeige nach Ansichtstyp
### Tabellenansichten
* Fields appear as columns
* Resize columns by dragging borders
* Felder werden als Spalten angezeigt
* Ändern Sie die Größe der Spalten, indem Sie die Ränder ziehen
### Kanban-Ansichten
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* Felder werden auf Karten angezeigt
* Über Optionen → Felder neu anordnen
* Verwenden Sie die Kompaktansicht, um alle Felder auszublenden
### Calendar Views
### Kalenderansichten
* Selected fields show on calendar events
* Configure via Options → Fields
* Ausgewählte Felder werden in Kalendereinträgen angezeigt
* Über Optionen → Felder konfigurieren
## Beste Praktiken
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **Nur das Nötige anzeigen** — zu viele Felder machen die Ansicht unübersichtlich
* **Wichtige Felder zuerst platzieren** — die am häufigsten verwendeten Spalten links
* **Mehrere Ansichten erstellen** — unterschiedliche Feldsätze für unterschiedliche Zwecke
* **Feldsichtbarkeit pro Ansicht verwenden** — gleiches Objekt, anderer Fokus
## Related
## Verwandt
* [Table Views](/l/de/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/de/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [Tabellenansichten](/l/de/user-guide/views-pipelines/capabilities/table-views) — Funktionen der Listenansicht
* [Kanban-Ansichten](/l/de/user-guide/views-pipelines/capabilities/kanban-views) — kartenbasierte Ansichten
@@ -1,6 +1,6 @@
---
title: Kanban Board Views
description: Learn how to use Kanban views to visualize and manage your workflows.
title: Kanban-Board-Ansichten
description: Erfahren Sie, wie Sie Kanban-Ansichten nutzen, um Ihre Workflows zu visualisieren und zu verwalten.
image: /images/user-guide/kanban-views/kanban.png
---
@@ -14,9 +14,9 @@ Kanban-Ansichten visualisieren Prozessabläufe, wobei jede Spalte eine eigene Ph
Sie können jede Karte zwischen den Phasen in Ihrem Workflow durch Ziehen und Loslassen verschieben. Um fortzufahren, halten Sie einen Mausklick auf einer Karte und verschieben Sie sie zur nächsten Phase.
<VimeoEmbed videoId="927888627" title="Video demonstration" />
<VimeoEmbed videoId="927888627" title="Video-Demonstration" />
## Add and Delete Stages
## Phasen hinzufügen und löschen
Sie können Ihren Workflow anpassen, indem Sie Phasen verwenden, die einen Wert in einem Auswahlfeld darstellen:
@@ -24,15 +24,15 @@ Sie können Ihren Workflow anpassen, indem Sie Phasen verwenden, die einen Wert
Um eine Phase hinzuzufügen, greifen Sie auf die Einstellungen des Auswahlfelds zu, indem Sie zu Einstellungen > Datenmodell navigieren, Ihr Objekt auswählen und dann das Feld, auf dem Ihr Kanban-Board basiert.
<VimeoEmbed videoId="927890428" title="Video demonstration" />
<VimeoEmbed videoId="927890428" title="Video-Demonstration" />
### Phasen entfernen
To remove a stage, hover the stage name or the `⋮` icon, click `Edit from settings` in the Select field settings, and then click **Delete** next to the relevant stage.
Um eine Phase zu entfernen, bewegen Sie den Mauszeiger über den Phasennamen oder das `⋮`-Symbol, klicken Sie in den Einstellungen des Auswahlfelds auf `Aus Einstellungen bearbeiten` und klicken Sie dann auf **Löschen** neben der entsprechenden Phase.
## Display Fields
## Felder anzeigen
Sie können Ihr Kanban-Board so konfigurieren, dass einige Felder angezeigt und andere ausgeblendet werden. To hide a field, click on **Options** on the top right, then on **Fields** to bring up the list of options. Look for the field needed in the Hidden Fields section and click on the eye button to display the field.
Sie können Ihr Kanban-Board so konfigurieren, dass einige Felder angezeigt und andere ausgeblendet werden. Um ein Feld auszublenden, klicken Sie oben rechts auf **Optionen** und dann auf **Felder**, um die Liste der Optionen anzuzeigen. Suchen Sie das benötigte Feld im Bereich Ausgeblendete Felder und klicken Sie auf die Schaltfläche mit dem Augensymbol, um das Feld anzuzeigen.
Sie können auch die Reihenfolge der Felder ändern, indem Sie den Feldnamen festhalten und dorthin ziehen, wo Sie ihn haben möchten.
@@ -40,60 +40,60 @@ Sie können auch die Reihenfolge der Felder ändern, indem Sie den Feldnamen fes
## Kompaktansicht
You can hide all the fields and get an overview of all records at a glance. To enable:
Sie können alle Felder ausblenden und einen Überblick über alle Datensätze auf einen Blick erhalten. So aktivieren Sie es:
1. Click **Options** on the top right
2. Turn on the toggle for **Compact view**
1. Klicken Sie oben rechts auf **Optionen**
2. Aktivieren Sie den Schalter für **Kompaktansicht**
<img src="/images/user-guide/kanban-views/compact-view.png" style={{width:'100%'}} />
## Column Aggregations
## Spaltenaggregationen
Each column in a Kanban view can display aggregated values at the top, helping you understand your data at a glance.
Jede Spalte in einer Kanban-Ansicht kann oben aggregierte Werte anzeigen, was Ihnen hilft, Ihre Daten auf einen Blick zu erfassen.
### Available Aggregations
### Verfügbare Aggregationen
| Aggregation | Beschreibung |
| ----------- | --------------------------------------------- |
| **Count** | Number of records in the column |
| **Sum** | Total of a numeric field (e.g., deal amounts) |
| **Average** | Average value of a numeric field |
| **Min** | Lowest value |
| **Max** | Highest value |
| Aggregation | Beschreibung |
| ---------------- | --------------------------------------------------------- |
| **Zählen** | Anzahl der Datensätze in der Spalte |
| **Summe** | Gesamtsumme eines numerischen Feldes (z. B. Deal-Beträge) |
| **Durchschnitt** | Durchschnittswert eines numerischen Feldes |
| **Min** | Niedrigster Wert |
| **Max** | Höchster Wert |
### Configuring Aggregations
### Aggregationen konfigurieren
1. Click on the number displayed next to the Stage value, at the top of a column
2. Select the aggregation type
3. Choose the field to aggregate
1. Klicken Sie oben in einer Spalte auf die Zahl, die neben dem Phasennamen angezeigt wird
2. Wählen Sie den Aggregationstyp aus
3. Wählen Sie das zu aggregierende Feld aus
**Example:** Show total deal value per stage by aggregating the Amount field with Sum.
**Beispiel:** Zeigen Sie den Gesamtwert der Deals pro Phase an, indem Sie das Feld Betrag mit Summe aggregieren.
## When to Use Kanban Views
## Wann Sie Kanban-Ansichten verwenden sollten
Kanban views are ideal for:
Kanban-Ansichten eignen sich ideal für:
* **Sales pipelines**: Track deals through stages from lead to close
* **Project management**: Monitor tasks through workflow states
* **Recruitment**: Track candidates through hiring stages
* **Any staged process**: Visualize any workflow with defined stages
* **Vertriebspipelines**: Verfolgen Sie Deals durch die Phasen vom Lead bis zum Abschluss
* **Projektmanagement**: Überwachen Sie Aufgaben anhand von Workflow-Status
* **Recruiting**: Verfolgen Sie Kandidaten durch die Einstellungsphasen
* **Jeder mehrstufige Prozess**: Visualisieren Sie jeden Workflow mit definierten Phasen
## Beste Praktiken
### Organize Your Stages
### Organisieren Sie Ihre Phasen
* **Limit stages**: 5-7 stages is ideal for visibility
* **Clear naming**: Use descriptive stage names
* **Logical order**: Arrange stages in process order
* **Phasen begrenzen**: 5-7 Phasen sind ideal für die Übersichtlichkeit
* **Klare Benennung**: Verwenden Sie aussagekräftige Phasennamen
* **Logische Reihenfolge**: Ordnen Sie Phasen in Prozessreihenfolge an
### Optimize Card Display
### Kartendarstellung optimieren
* **Show key fields**: Display only the most important information
* **Use compact view**: For high-level overviews
* **Color coding**: Use stage colors to quickly identify status
* **Schlüsselfelder anzeigen**: Zeigen Sie nur die wichtigsten Informationen an
* **Kompaktansicht verwenden**: Für Übersichten auf hoher Ebene
* **Farbkodierung**: Nutzen Sie Phasenfarben, um den Status schnell zu erkennen
### Maintain Data Quality
### Datenqualität sicherstellen
* **Update regularly**: Keep cards moving through stages
* **Archive completed**: Move closed items out of active view
* **Review stale cards**: Follow up on cards stuck in stages
* **Regelmäßig aktualisieren**: Sorgen Sie dafür, dass Karten durch die Phasen fortschreiten
* **Abgeschlossenes archivieren**: Verschieben Sie geschlossene Elemente aus der aktiven Ansicht
* **Veraltete Karten prüfen**: Fassen Sie bei Karten nach, die in Phasen feststecken
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Autenticazione
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Scopo |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Note:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Autentificare
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Scop |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Note:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: Întrebări frecvente despre IA
description: Întrebări frecvente despre funcționalitățile IA din Twenty.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="Când vor fi disponibile funcționalitățile IA?">
Funcționalitățile IA sunt în prezent în dezvoltare și vor fi lansate în curând în versiune beta. Rămâneți la curent cu actualizările!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="Ce capabilități IA sunt planificate?">
Dezvoltăm două capabilități IA principale:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **Chatbot IA**: Un asistent conștient de context care poate accesa datele dvs. din Twenty și vă poate ajuta cu întrebări
2. **Agenți IA în fluxuri de lucru**: Automatizare inteligentă care poate procesa date, lua decizii și executa sarcini în cadrul fluxurilor dvs. de lucru
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="Vor avea agenții IA acces la toate datele mele?">
Agenții IA vor funcționa conform sistemului de permisiuni. Puteți atribui agenților IA roluri specifice în **Settings → Roles**, oferindu-vă control total asupra datelor la care pot avea acces și a acțiunilor pe care le pot efectua.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="Cum vor funcționa creditele IA?">
Acțiunile IA vor consuma credite pentru fluxuri de lucru în funcție de complexitatea sarcinii și de modelul IA utilizat. Mai multe detalii vor fi disponibile la lansarea funcționalităților.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="Pot folosi propriile modele IA?">
Inițial, Twenty va utiliza modele IA integrate. Suportul pentru modele IA personalizate sau externe poate fi adăugat în versiunile viitoare, pe baza feedbackului utilizatorilor.
</Accordion>
</AccordionGroup>
@@ -180,22 +180,22 @@ Consultați [Cum să actualizați înregistrările existente](/l/ro/user-guide/d
</Accordion>
<Accordion title="Pot importa companii fără nicio Persoană asociată?">
Da! You can import companies first, then import People later and link them using the company domain.
Da! Puteți importa mai întâi companiile, apoi să importați ulterior Persoane și să le asociați folosind domeniul companiei.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="Ce se întâmplă dacă import un domeniu care există deja?">
Dacă includeți un identificator unic (domeniu sau id) care se potrivește unei companii existente, Twenty actualizează acea companie în loc să creeze un duplicat.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="Cum remediez erorile 'duplicate domain'?">
Eliminați duplicatul din fișier sau includeți `id`-ul companiei pentru a actualiza în schimb înregistrarea existentă.
</Accordion>
</AccordionGroup>
## Depanare
Having issues? Check:
Aveți probleme? Verificați:
* [How to Fix Import Errors](/l/ro/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/ro/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/ro/user-guide/data-migration/capabilities/uniqueness-constraints)
* [Cum să remediați erorile de import](/l/ro/user-guide/data-migration/how-tos/fix-import-errors)
* [Referință pentru maparea câmpurilor](/l/ro/user-guide/data-migration/capabilities/field-mapping)
* [Constrângeri de unicitate](/l/ro/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: Câmpuri de relație
description: Connect records across different objects using relation fields.
description: Conectați înregistrări din obiecte diferite folosind câmpuri de relație.
---
## Types of Relations
## Tipuri de relații
### One-to-Many
### Unu-la-mulți
One record in Object A can be linked to many records in Object B.
O înregistrare din Obiectul A poate fi legată de multe înregistrări din Obiectul B.
**Example:** One Company can have many People (employees).
**Exemplu:** O Companie poate avea multe Persoane (angajați).
### Many-to-One
### Mulți-la-unu
Many records in Object A can be linked to one record in Object B.
Multe înregistrări din Obiectul A pot fi legate de o singură înregistrare din Obiectul B.
**Example:** Many People can belong to one Company.
**Exemplu:** Multe Persoane pot aparține unei singure Companii.
### Relations to Multiple Object Types
### Relații către mai multe tipuri de obiecte
Some objects can link to multiple object types on one side of the relation.
Unele obiecte pot face legătura către mai multe tipuri de obiecte pe o parte a relației.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**Exemplu:** O Notă poate fi atașată simultan la o Persoană ȘI o Companie ȘI o Oportunitate. Obiectul Notă este pe partea "mulți", conectându-se la mai multe părți "unu".
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
În mod similar, un Proiect (pe partea "unu") ar putea primi legături de la mai multe Persoane, mai multe Companii și mai multe Note.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**Limitare la import/export**: Relațiile care indică către mai multe tipuri de obiecte nu sunt încă acceptate pentru importul/exportul CSV. Această funcționalitate este în planul nostru de dezvoltare.
</Warning>
### Many-to-Many
### Mulți-la-mulți
Many records in Object A can be linked to many records in Object B.
Multe înregistrări din Obiectul A pot fi legate de multe înregistrări din Obiectul B.
**Example:** Many People can be linked to many Projects, and vice versa.
**Exemplu:** Multe Persoane pot fi legate de multe Proiecte și invers.
<Warning>
**Many-to-Many is not yet supported.**
**Mulți-la-mulți nu este încă acceptat.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
Acest tip de relație este planificat pentru S1 2026. Ca soluție temporară, creați un obiect intermediar de legătură (de ex., „Atribuiri de proiect”) care are relații mulți-la-unu cu ambele obiecte.
</Warning>
## Creating a Relation Field
## Crearea unui câmp de relație
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. Accesați **Setări → Model de date**
2. Selectați obiectul în care doriți să adăugați relația
3. Faceți clic pe **+ Adaugă câmp**
4. Selectați **Relație** ca tip de câmp
5. Alegeți obiectul/obiectele țintă cu care să stabiliți relația
6. Configurați setările relației:
* **Numele câmpului pe obiectul sursă**: Numele câmpului de relație pe obiectul pe care îl editați
* **Numele câmpului pe obiectul destinație**: Numele câmpului de relație care va apărea pe obiectul țintă
* Tipul relației (unu-la-mulți, mulți-la-unu)
7. Faceți clic pe **Salvare**
## Standard Relations
## Relații standard
Twenty comes with pre-built relations between standard objects:
Twenty include relații predefinite între obiectele standard:
| From Object | To Object | Relation Type |
| ------------ | --------- | ------------- |
| Persoane | Companii | Many-to-One |
| Oportunități | Companii | Many-to-One |
| Oportunități | Persoane | Many-to-One |
| Din obiect | Către obiect | Tipul relației |
| ------------ | ------------ | -------------- |
| Persoane | Companii | Mulți-la-unu |
| Oportunități | Companii | Mulți-la-unu |
| Oportunități | Persoane | Mulți-la-unu |
## Cele mai bune practici
### Planning Relations
### Planificarea relațiilor
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **Mapați modelul de date**: Planificați relațiile înainte de a le crea
* **Luați în considerare direcția**: Gândiți-vă care obiect „deține” relația
* **Evitați dependențele circulare**: Păstrați modelul de date curat
### Naming Relations
### Denumierea relațiilor
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **Folosiți denumiri clare**: Să fie evident ce reprezintă relația
* **Fiți consecvenți**: Folosiți tipare de denumire similare pentru toate relațiile
* **Luați în considerare ambele părți**: Denumiți corespunzător ambele părți ale relației
### Performance
### Performanță
* **Don't over-relate**: Too many relations can slow down your workspace
* **Nu exagerați cu relațiile**: Prea multe relații pot încetini spațiul de lucru
## Limitations
## Limitări
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **Ștergerea relațiilor** elimină legătura, dar nu și înregistrările asociate
* **Relațiile circulare** ar trebui evitate pentru integritatea datelor
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: Creați câmpuri personalizate
description: Ghid pas cu pas pentru adăugarea câmpurilor personalizate la orice obiect.
---
Custom fields let you capture information specific to your business. Add them to any objectstandard or custom.
Câmpurile personalizate vă permit să colectați informații specifice afacerii dumneavoastră. Adăugați-le la orice obiectstandard sau personalizat.
## Steps
## Pași
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/ro/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. Accesați **Setări → Model de date**
2. Selectați obiectul la care doriți să adăugați un câmp
3. Faceți clic pe **+ Adaugă câmp**
4. Alegeți un **tip de câmp** (consultați [Câmpuri](/l/ro/user-guide/data-model/capabilities/fields) pentru toate tipurile)
5. Introduceți **numele câmpului** și o descriere opțională
6. Configurați setările specifice câmpului (vedeți mai jos)
7. Faceți clic pe **Salvare**
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**Metodă rapidă:** Faceți clic pe **+** la capătul anteturilor de coloană din orice vizualizare de tabel → **Personalizați câmpurile**.
## Show the Field in Views
## Afișați câmpul în vizualizări
New fields aren't automatically visible. To display:
Noile câmpuri nu sunt vizibile automat. Pentru a afișa:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. Deschideți vizualizarea tabelară a obiectului
2. Faceți clic pe **Opțiuni → Câmpuri**
3. Faceți clic pe **pictograma în formă de ochi** de lângă câmpul dvs. pentru a-l afișa
4. Trageți pentru a reordona
## Configuration Options
## Opțiuni de configurare
### For Select / Multi-Select
### Pentru Selectare / Selectare multiplă
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. Faceți clic pe **+ Adaugă opțiune** pentru a crea opțiuni
2. Stabiliți o **opțiune implicită** dacă doriți
3. Trageți pentru a reordona opțiunile
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/ro/user-guide/data-migration/capabilities/field-mapping).
**Folosiți numele API pentru importuri.** Activați **modul Avansat** în Setări pentru a vedea numele API. Consultați [Maparea câmpurilor](/l/ro/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### Pentru câmpurile de monedă
Set the **default currency** (USD, EUR, etc.) for new records.
Setați **moneda implicită** (USD, EUR, etc.) pentru noile înregistrări.
### For Phone Fields
### Pentru câmpurile de telefon
Set the **default country code** to pre-fill for new phone numbers.
Setați **codul de țară implicit** care să fie precompletat pentru noile numere de telefon.
### Making a Field Unique
### Setarea unui câmp ca unic
Toggle **Unique** to prevent duplicate values across records.
Comutați **Unic** pentru a preveni valorile duplicate în înregistrări.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
Dacă există duplicate (inclusiv în înregistrările șterse), veți primi o eroare. Eliminați mai întâi duplicatele.
</Note>
### Setting Default Values
### Setarea valorilor implicite
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
Pentru câmpurile de tip Selectare, puteți alege care opțiune este preselectată pentru noile înregistrări. Pentru câmpurile de tip Casetă de bifare, setați dacă este bifată sau debifată în mod implicit.
## Deactivating a Field
## Dezactivarea unui câmp
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. Accesați **Setări → Model de date**
2. Găsiți câmpul
3. Faceți clic pe **⋮ → Dezactivează**
Data is preserved. You can reactivate or permanently delete later.
Datele sunt păstrate. Puteți reactiva sau șterge definitiv mai târziu.
## Related
## Conexe
* [Fields](/l/ro/user-guide/data-model/capabilities/fields) — all field types explained
* [Câmpuri](/l/ro/user-guide/data-model/capabilities/fields) — toate tipurile de câmpuri explicate
* [Întrebări frecvente despre modelul de date](/l/ro/user-guide/data-model/how-tos/data-model-faq) — întrebări uzuale
@@ -1,6 +1,6 @@
---
title: Model de date
description: Learn what a data model is and how to design one that fits your business.
description: Află ce este un model de date și cum să proiectezi unul care se potrivește afacerii tale.
image: /images/user-guide/fields/custom_data_model.png
---
@@ -8,120 +8,120 @@ image: /images/user-guide/fields/custom_data_model.png
<img src="/images/user-guide/fields/custom_data_model.png" alt="Model de date" />
</Frame>
## What is a Data Model?
## Ce este un model de date?
Un model de date este structura care definește cum sunt organizate informațiile în CRM-ul tău. Think of it as the **blueprint** of your customer data — you design it once, then fill it with your actual data.
Un model de date este structura care definește cum sunt organizate informațiile în CRM-ul tău. Gândește-te la el ca la **planul** datelor tale despre clienți — îl proiectezi o singură dată, apoi îl umpli cu datele tale reale.
## Key Concepts
## Concepte cheie
### Obiecte
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
**Obiectele** sunt principalele categorii de date din CRM-ul tău. Fiecare obiect reprezintă un tip de element pe care vrei să îl urmărești.
Twenty comes with standard objects:
Twenty include obiecte standard:
* **People** — individuals (contacts, leads, partners)
* **Companies** — organizations
* **Opportunities** — deals or sales
* **Notes** — attached notes on records
* **Tasks** — to-dos linked to records
* **People** — persoane (contacte, leaduri, parteneri)
* **Companii** — organizații
* **Oportunități** — tranzacții sau vânzări
* **Note** — note atașate la înregistrări
* **Sarcini** — sarcini legate de înregistrări
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
Poți crea și **obiecte personalizate** pentru orice este specific afacerii tale (de ex., Proiecte, Abonamente, Evenimente).
### Câmpuri
**Fields** are the properties or attributes that describe each object. They store the actual information.
**Câmpurile** sunt proprietățile sau atributele care descriu fiecare obiect. Ele stochează informațiile efective.
For example, the **People** object has fields like:
De exemplu, obiectul **People** are câmpuri precum:
* Nume
* Email
* Telefon
* Job Title
* Company (a relation to the Companies object)
* Funcție
* Companie (o relație cu obiectul Companies)
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
Câmpurile au diferite **tipuri**: text, număr, dată, selectare, selecție multiplă, relație și altele. Poți adăuga câmpuri personalizate la orice obiect.
### Înregistrări
**Records** are the individual entries within an object — the actual data you create and manage.
**Înregistrările** sunt intrările individuale dintr-un obiect — datele efective pe care le creezi și le gestionezi.
De exemplu:
* "John Smith" is a **record** in the People object
* "Acme Corp" is a **record** in the Companies object
* "John Smith" este o **înregistrare** în obiectul People
* "Acme Corp" este o **înregistrare** în obiectul Companies
**An analogy:**
**O analogie:**
| Data Model Concept | Real-World Analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **Câmpuri** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
| Conceptul de model de date | Analogie din lumea reală |
| -------------------------- | ------------------------------------------------------- |
| **Obiecte** | Secțiuni într-o carte (categoriile) |
| **Câmpuri** | Coloane într-o foaie de calcul (proprietățile) |
| **Înregistrări** | Rânduri într-o foaie de calcul (intrările propriu-zise) |
You design the data model (objects + fields) once, then create many records within that structure.
Proiectezi modelul de date (obiecte + câmpuri) o singură dată, apoi creezi multe înregistrări în acea structură.
## Why Customize Your Data Model?
## De ce să-ți personalizezi modelul de date?
Fiecare afacere funcționează diferit. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
Fiecare afacere funcționează diferit. Personalizarea modelului tău de date înseamnă că poți modela Twenty în jurul proceselor **tale**, în loc să le forțezi într-un sistem rigid.
Twenty offers full flexibility:
Twenty oferă flexibilitate totală:
* Create as many custom objects as you need
* Add unlimited custom fields
* The price doesn't change based on customization
* Creează oricâte obiecte personalizate de care ai nevoie
* Adaugă un număr nelimitat de câmpuri personalizate
* Prețul nu se schimbă în funcție de personalizare
## Tips to Design Your Data Model
## Sfaturi pentru a proiecta modelul tău de date
### 1. Start with Your Core Objects
### 1. Începe cu obiectele tale de bază
Identify the main concepts you work with. Twenty already provides:
Identifică principalele concepte cu care lucrezi. Twenty oferă deja:
* **People** — your contacts
* **Companies** — your accounts
* **Opportunities** — your deals
* **People** — contactele tale
* **Companies** — conturile tale
* **Oportunități** — tranzacțiile tale
Think about what else you might need:
Gândește-te la ce altceva ai putea avea nevoie:
* Stripe would need a `Subscriptions` object
* Airbnb would need a `Trips` object
* An accelerator would need a `Batches` object
* Stripe ar avea nevoie de un obiect `Subscriptions`
* Airbnb ar avea nevoie de un obiect `Trips`
* Un accelerator ar avea nevoie de un obiect `Batches`
### 2. Use Fields for Variations, Not New Objects
### 2. Folosește câmpuri pentru variații, nu obiecte noi
If something is just a characteristic of an existing object, make it a **field**.
Dacă ceva este doar o caracteristică a unui obiect existent, fă-l un **câmp**.
**Use fields for:**
**Folosește câmpuri pentru:**
* Categories and labels (e.g., `Industry` for Companies)
* Status values (e.g., `Stage` for Opportunities)
* Attributes and properties
* Categorii și etichete (de ex., `Industry` pentru Companies)
* Valori de stare (de ex., `Stage` pentru Opportunities)
* Atribute și proprietăți
### 3. Create an Object When It Stands on Its Own
### 3. Creează un obiect atunci când stă pe cont propriu
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
Dacă conceptul are propriul ciclu de viață, propriile proprietăți sau relații, merită un obiect.
**Create an object for:**
**Creează un obiect pentru:**
* **Projects** — have deadlines, owners, and tasks
* **Subscriptions** — connect companies, products, and invoices
* **Events** — involve attendees and follow-up actions
* **Proiecte** — au termene limită, responsabili și sarcini
* **Abonamente** — conectează companii, produse și facturi
* **Evenimente** — implică participanți și acțiuni ulterioare
Acestea depășesc un singur câmp deoarece au propriile date și relații.
### 4. Create an Object When Records Are Open-Ended
### 4. Creează un obiect atunci când numărul înregistrărilor este nedeterminat
If something can be linked multiple times and you don't know how many, use an object.
Dacă ceva poate fi asociat de mai multe ori și nu știi de câte, folosește un obiect.
**Bad approach:**
Creating fields like `Product 1`, `Product 2`, `Product 3`...
**Abordare greșită:**
Crearea de câmpuri precum `Product 1`, `Product 2`, `Product 3`...
**Good approach:**
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
**Abordare bună:**
Creează un obiect `Products` și relaționează-l cu înregistrările. Astfel, poți susține unul, două sau o sută de produse fără a-ți schimba modelul.
### 5. Keep It Simple First
### 5. Păstrează totul simplu la început
Start with fields. Move to new objects only when you feel the limits:
Începe cu câmpurile. Treci la obiecte noi doar când simți limitele:
* Too many fields on one object
* Repeated records that should be separate
@@ -3,7 +3,7 @@ title: Setări Spațiu de Lucru
description: Personalizează numele și brandingul spațiului tău de lucru.
---
Those are accessible under **Settings → General**.
Acestea sunt accesibile din **Setări → General**.
## Imaginea Spațiului de Lucru
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: Câmpuri & coloane
description: Alegeți ce câmpuri să afișați și cum să le organizați.
---
## Selecting Fields to Display
## Selectarea câmpurilor de afișat
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
Fiecare vizualizare poate afișa un set diferit de câmpuri. Personalizați ceea ce este vizibil pentru a vă concentra pe informațiile care contează.
### Show or Hide Fields
### Afișați sau ascundeți câmpuri
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. Faceți clic pe **Opțiuni** în dreapta sus
2. Faceți clic pe **Câmpuri**
3. Faceți clic pe **pictograma în formă de ochi** de lângă fiecare câmp pentru a-l afișa/ascunde
### Reorder Fields
### Reordonați câmpurile
Change the order fields appear in your view:
Modificați ordinea în care câmpurile apar în vizualizarea dvs.:
1. Click **Options → Fields**
2. Drag fields up or down
3. Changes save automatically
1. Faceți clic pe **Opțiuni → Câmpuri**
2. Glisați câmpurile în sus sau în jos
3. Modificările sunt salvate automat
## Field Display by View Type
## Afișarea câmpurilor în funcție de tipul vizualizării
### Vizualizări de Tabel
* Fields appear as columns
* Resize columns by dragging borders
* Câmpurile apar sub formă de coloane
* Redimensionați coloanele trăgând marginile
### Vizualizări Kanban
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* Câmpurile apar pe carduri
* Reordonați din Opțiuni → Câmpuri
* Folosiți vizualizarea compactă pentru a ascunde toate câmpurile
### Calendar Views
### Vizualizări de calendar
* Selected fields show on calendar events
* Configure via Options → Fields
* Câmpurile selectate se afișează în evenimentele din calendar
* Configurați din Opțiuni → Câmpuri
## Cele mai bune practici
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **Afișați doar ceea ce este necesar** — prea multe câmpuri aglomerează vizualizarea
* **Puneți câmpurile importante primele** — coloanele cele mai utilizate în stânga
* **Creați mai multe vizualizări** — seturi de câmpuri diferite pentru scopuri diferite
* **Folosiți vizibilitatea câmpurilor pentru fiecare vizualizare** — același obiect, accent diferit
## Related
## Conexe
* [Table Views](/l/ro/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/ro/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [Vizualizări de Tabel](/l/ro/user-guide/views-pipelines/capabilities/table-views) — funcționalități ale vizualizării de listă
* [Vizualizări Kanban](/l/ro/user-guide/views-pipelines/capabilities/kanban-views) — vizualizări bazate pe carduri
@@ -1,6 +1,6 @@
---
title: Kanban Board Views
description: Learn how to use Kanban views to visualize and manage your workflows.
title: Vizualizări pentru tabloul Kanban
description: Aflați cum să utilizați vizualizările Kanban pentru a vizualiza și gestiona fluxurile de lucru.
image: /images/user-guide/kanban-views/kanban.png
---
@@ -8,15 +8,15 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## Despre vizualizările Kanban
Kanban views visually map out process flows, where each column stands for a distinct stage and each card represents a record.
Vizualizările Kanban cartografiază vizual fluxurile de proces, unde fiecare coloană reprezintă o etapă distinctă și fiecare card reprezintă o înregistrare.
## Mută Carduri între Etape
You can move each card between stages as it goes through your workflow by dragging and dropping. Pentru a continua, ține apăsat pe un card și mută-l la etapa următoare.
Puteți muta fiecare card între etape pe măsură ce parcurge fluxul de lucru, prin glisare și fixare. Pentru a continua, ține apăsat pe un card și mută-l la etapa următoare.
<VimeoEmbed videoId="927888627" title="Demonstrație video" />
## Add and Delete Stages
## Adăugați și ștergeți etape
Poți personaliza fluxul de lucru pentru a se potrivi nevoilor tale folosind etape, care reprezintă o valoare într-un Câmp Selectează:
@@ -28,11 +28,11 @@ Pentru a adăuga o etapă, accesează setările câmpului Selectează navigând
### Elimină Etape
To remove a stage, hover the stage name or the `⋮` icon, click `Edit from settings` in the Select field settings, and then click **Delete** next to the relevant stage.
Pentru a elimina o etapă, treceți cursorul peste numele etapei sau pictograma `⋮`, faceți clic pe `Edit from settings` în setările câmpului Select și apoi faceți clic pe **Șterge** lângă etapa relevantă.
## Display Fields
## Afișare câmpuri
Poți configura panoul Kanban pentru a afișa unele câmpuri și a ascunde altele. To hide a field, click on **Options** on the top right, then on **Fields** to bring up the list of options. Look for the field needed in the Hidden Fields section and click on the eye button to display the field.
Poți configura panoul Kanban pentru a afișa unele câmpuri și a ascunde altele. Pentru a ascunde un câmp, faceți clic pe **Opțiuni** în colțul din dreapta sus, apoi pe **Câmpuri** pentru a deschide lista de opțiuni. Căutați câmpul necesar în secțiunea Câmpuri ascunse și faceți clic pe butonul în formă de ochi pentru a afișa câmpul.
Poți, de asemenea, să rearanjezi ordinea câmpurilor ținând apăsat numele câmpului și trăgându-l acolo unde îl dorești.
@@ -40,60 +40,60 @@ Poți, de asemenea, să rearanjezi ordinea câmpurilor ținând apăsat numele c
## Vizualizare Compactă
You can hide all the fields and get an overview of all records at a glance. To enable:
Puteți ascunde toate câmpurile și obține o imagine de ansamblu a tuturor înregistrărilor dintr-o privire. Pentru activare:
1. Click **Options** on the top right
2. Turn on the toggle for **Compact view**
1. Faceți clic pe **Opțiuni** în dreapta sus
2. Activați comutatorul pentru **Vizualizare compactă**
<img src="/images/user-guide/kanban-views/compact-view.png" style={{width:'100%'}} />
## Column Aggregations
## Agregări pe coloană
Each column in a Kanban view can display aggregated values at the top, helping you understand your data at a glance.
Fiecare coloană dintr-o vizualizare Kanban poate afișa valori agregate în partea de sus, ajutându-vă să înțelegeți datele dintr-o privire.
### Available Aggregations
### Agregări disponibile
| Aggregation | Descriere |
| ----------- | --------------------------------------------- |
| **Count** | Number of records in the column |
| **Sum** | Total of a numeric field (e.g., deal amounts) |
| **Average** | Average value of a numeric field |
| **Min** | Lowest value |
| **Max** | Highest value |
| Agregare | Descriere |
| --------- | -------------------------------------------------------- |
| **Număr** | Numărul de înregistrări din coloană |
| **Sumă** | Totalul unui câmp numeric (de ex., sumele tranzacțiilor) |
| **Medie** | Valoarea medie a unui câmp numeric |
| **Minim** | Cea mai mică valoare |
| **Maxim** | Cea mai mare valoare |
### Configuring Aggregations
### Configurarea agregărilor
1. Click on the number displayed next to the Stage value, at the top of a column
2. Select the aggregation type
3. Choose the field to aggregate
1. Faceți clic pe numărul afișat lângă valoarea etapei, în partea de sus a unei coloane
2. Selectați tipul de agregare
3. Alegeți câmpul pe care să îl agregați
**Example:** Show total deal value per stage by aggregating the Amount field with Sum.
**Exemplu:** Afișați valoarea totală a tranzacțiilor pe etapă prin agregarea câmpului Sumă cu operația Sumă.
## When to Use Kanban Views
## Când să utilizați vizualizările Kanban
Kanban views are ideal for:
Vizualizările Kanban sunt ideale pentru:
* **Sales pipelines**: Track deals through stages from lead to close
* **Project management**: Monitor tasks through workflow states
* **Recruitment**: Track candidates through hiring stages
* **Any staged process**: Visualize any workflow with defined stages
* **Pipeline-uri de vânzări**: Urmăriți tranzacțiile prin etape, de la lead până la închidere
* **Managementul proiectelor**: Monitorizați sarcinile prin stările fluxului de lucru
* **Recrutare**: Urmăriți candidații prin etapele de angajare
* **Orice proces etapizat**: Vizualizați orice flux de lucru cu etape definite
## Cele mai bune practici
### Organize Your Stages
### Organizați-vă etapele
* **Limit stages**: 5-7 stages is ideal for visibility
* **Clear naming**: Use descriptive stage names
* **Logical order**: Arrange stages in process order
* **Limitați etapele**: 5-7 etape sunt ideale pentru vizibilitate
* **Denumire clară**: Folosiți denumiri de etape descriptive
* **Ordine logică**: Aranjați etapele în ordinea procesului
### Optimize Card Display
### Optimizați afișarea cardurilor
* **Show key fields**: Display only the most important information
* **Use compact view**: For high-level overviews
* **Color coding**: Use stage colors to quickly identify status
* **Afișați câmpurile cheie**: Afișați doar cele mai importante informații
* **Utilizați vizualizarea compactă**: Pentru vederi de ansamblu la nivel înalt
* **Coduri de culoare**: Folosiți culorile etapelor pentru a identifica rapid starea
### Maintain Data Quality
### Mențineți calitatea datelor
* **Update regularly**: Keep cards moving through stages
* **Archive completed**: Move closed items out of active view
* **Review stale cards**: Follow up on cards stuck in stages
* **Actualizați regulat**: Mențineți cardurile în mișcare prin etape
* **Arhivați elementele finalizate**: Mutați elementele închise în afara vizualizării active
* **Revizuiți cardurile învechite**: Reluați urmărirea cardurilor blocate în etape
@@ -1,140 +1,140 @@
---
title: Closed Won Automations
description: Automate post-win activities when opportunities close.
title: Automatizări pentru Closed Won
description: Automatizați activitățile de după câștig atunci când oportunitățile se închid.
---
When a deal closes, multiple things need to happen: update company status, notify team members, create onboarding tasks. Automate all of this with a single workflow.
Când o tranzacție se închide, trebuie să se întâmple mai multe lucruri: actualizați starea companiei, notificați membrii echipei, creați sarcini de onboarding. Automatizați totul cu un singur flux de lucru.
## The Problem
## Problema
When an opportunity moves to "Closed Won":
Atunci când o oportunitate trece la "Closed Won":
* Company type needs to change from "Prospect" to "Customer"
* Onboarding tasks need to be created
* Customer success team needs to be notified
* Sales rep needs confirmation
* Tipul companiei trebuie să se schimbe din "Prospect" în "Customer"
* Trebuie create sarcini de onboarding
* Echipa de Customer Success trebuie notificată
* Reprezentantul de vânzări are nevoie de confirmare
Doing this manually is time-consuming and error-prone.
Efectuarea manuală a acestor pași consumă timp și este predispusă la erori.
## The Solution
## Soluția
Create a workflow that handles all post-win activities automatically.
Creați un flux de lucru care gestionează automat toate activitățile de după câștig.
## Complete Workflow Setup
## Configurare completă a fluxului de lucru
### Step 1: Create the Workflow
### Pasul 1: Creați fluxul de lucru
1. Go to **Settings → Workflows**
2. Click **+ New Workflow**
3. Name it "Deal Won - Post-Win Automation"
1. Accesați **Setări → Fluxuri de lucru**
2. Faceți clic pe **+ Flux de lucru nou**
3. Denumiți-l "Deal Won - Post-Win Automation"
### Step 2: Configure the Trigger
### Pasul 2: Configurați declanșatorul
1. Select **Record is Updated**
2. Choose **Opportunities**
3. Under "Fields to monitor", select **Stage**
1. Selectați **Record is Updated**
2. Alegeți **Oportunități**
3. La "Fields to monitor", selectați **Stage**
### Step 3: Add Stage Filter
### Pasul 3: Adăugați filtrul de stadiu
1. Add **Filter** action
2. Condition: `{{trigger.object.stage}}` equals "Closed Won"
1. Adăugați acțiunea **Filter**
2. Condiție: `{{trigger.object.stage}}` este egal cu "Closed Won"
### Step 4: Update Company Type
### Pasul 4: Actualizați tipul companiei
1. Add **Update Record** action
1. Adăugați acțiunea **Update Record**
2. Configurați:
| Câmp | Valoare |
| ------------------------- | ------------------------------- |
| **Object** | Companii |
| **Record** | `{{trigger.object.company.id}}` |
| **Tip** | Client |
| **First Deal Date** | `{{trigger.object.closedAt}}` |
| **Proprietarul contului** | `{{trigger.object.owner.id}}` |
| Câmp | Valoare |
| -------------------------- | ------------------------------- |
| **Obiect** | Companii |
| **Înregistrare** | `{{trigger.object.company.id}}` |
| **Tip** | Client |
| **Data primei tranzacții** | `{{trigger.object.closedAt}}` |
| **Proprietarul contului** | `{{trigger.object.owner.id}}` |
### Step 5: Create Onboarding Task
### Pasul 5: Creați o sarcină de onboarding
1. Add **Create Record** action
1. Adăugați acțiunea **Create Record**
2. Configurați:
| Câmp | Valoare |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Object** | Sarcini |
| **Title** | `Onboarding: {{trigger.object.name}}` |
| **Assignee** | Customer Success team member |
| **Due Date** | 3 days from now |
| **Priority** | High |
| **Related Company** | `{{trigger.object.company.id}}` |
| **Related Opportunity** | `{{trigger.object.id}}` |
| **Description** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
| Câmp | Valoare |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Obiect** | Sarcini |
| **Titlu** | `Onboarding: {{trigger.object.name}}` |
| **Responsabil** | Membru al echipei de Customer Success |
| **Data scadenței** | Peste 3 zile |
| **Prioritate** | Ridicată |
| **Companie asociată** | `{{trigger.object.company.id}}` |
| **Oportunitate asociată** | `{{trigger.object.id}}` |
| **Descriere** | `Onboarding pentru noul client {{trigger.object.company.name}}. Valoarea tranzacției: {{trigger.object.amount}}` |
### Step 6: Notify Customer Success
### Pasul 6: Notificați echipa de Customer Success
1. Add **Send Email** action
1. Adăugați acțiunea **Send Email**
2. Configurați:
| Câmp | Valoare |
| ----------- | -------------------------------------------------- |
| **To** | customer-success@yourcompany.com |
| **Subject** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Body** | See example below |
| Câmp | Valoare |
| ----------- | ------------------------------------------------ |
| **Către** | customer-success@yourcompany.com |
| **Subiect** | `🎉 Client nou: {{trigger.object.company.name}}` |
| **Corp** | Consultați exemplul de mai jos |
**Email body example**:
**Exemplu de corp al e-mailului**:
```
Hi CS Team,
Bună, echipă CS,
We have a new customer!
Avem un client nou!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
Companie: {{trigger.object.company.name}}
Tranzacție: {{trigger.object.name}}
Valoare: {{trigger.object.amount}}
Reprezentant de vânzări: {{trigger.object.owner.name}}
Data închiderii: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
O sarcină de onboarding a fost creată automat.
Let's give them a great start!
Să le oferim un început grozav!
```
### Step 7: Confirm to Sales Rep
### Pasul 7: Confirmare către reprezentantul de vânzări
1. Add another **Send Email** action
1. Adăugați încă o acțiune **Send Email**
2. Configurați:
| Câmp | Valoare |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| **To** | `{{trigger.object.owner.email}}` |
| **Subject** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Body** | Congratulations! Your deal has been processed. The customer success team has been notified and onboarding has begun. |
| Câmp | Valoare |
| ----------- | ----------------------------------------------------------------------------------------------------------------- |
| **Către** | `{{trigger.object.owner.email}}` |
| **Subiect** | `✅ Tranzacție închisă: {{trigger.object.name}}` |
| **Corp** | Felicitări! Tranzacția a fost procesată. Echipa de Customer Success a fost notificată și onboarding-ul a început. |
### Step 8: Test and Activate
### Pasul 8: Testați și activați
1. Test by moving a test opportunity to "Closed Won"
1. Testați mutând o oportunitate de test la "Closed Won"
2. Verifică:
* Company type changed to "Customer"
* Onboarding task created
* CS team received email
* Sales rep received confirmation
3. Activate when ready
* Tipul companiei schimbat la "Customer"
* Sarcină de onboarding creată
* Echipa de Customer Success a primit e-mailul
* Reprezentantul de vânzări a primit confirmarea
3. Activați când sunteți gata
## Handling Closed Lost
## Gestionarea Closed Lost
Create a similar workflow for lost deals:
Creați un flux de lucru similar pentru tranzacțiile pierdute:
### Declanșator
* Record is Updated (Opportunities, Stage = "Closed Lost")
* Record is Updated (Oportunități, Stage = "Closed Lost")
### Acțiuni
1. **Create Record**: Task for "Lost Deal Analysis"
2. **Update Record**: Add lost reason to company record
3. **Send Email**: Notify manager of lost deal
1. **Create Record**: Sarcină pentru "Lost Deal Analysis"
2. **Update Record**: Adăugați motivul pierderii în înregistrarea companiei
3. **Send Email**: Notificați managerul privind tranzacția pierdută
## Advanced: Multi-Step Onboarding
## Avansat: Onboarding în mai mulți pași
For complex onboarding, create multiple tasks:
Pentru onboarding complex, creați mai multe sarcini:
```javascript
export const main = async (params) => {
@@ -149,31 +149,31 @@ export const main = async (params) => {
};
```
Use **Iterator** to create each task from the array.
Utilizați **Iterator** pentru a crea fiecare sarcină din array.
## Customization Ideas
## Idei de personalizare
### Keep your other tools up-to-date
### Mențineți celelalte instrumente actualizate
* Create customer in billing system with an **HTTP Request**
* Creați un client în sistemul de facturare cu un **HTTP Request**
### Conditional Actions
### Acțiuni condiționale
Use **Filter** actions to:
Utilizați acțiunile **Filter** pentru:
* Different onboarding for enterprise vs SMB
* Different assignees based on region
* Skip notifications for small deals
* Onboarding diferit pentru enterprise vs IMM
* Responsabili diferiți în funcție de regiune
* Omiteți notificările pentru tranzacții mici
### Include Deal Details
### Includeți detaliile tranzacției
Use **Code** action to format:
Utilizați acțiunea **Code** pentru a formata:
* Deal summary documents
* Handoff notes for CS team
* Custom onboarding checklists
* Documente cu rezumatul tranzacției
* Note de predare pentru echipa de Customer Success
* Liste de verificare personalizate pentru onboarding
## Related
## Conexe
* [Workflow Actions](/l/ro/user-guide/workflows/capabilities/workflow-actions)
* [Send Emails from Workflows](/l/ro/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Acțiuni ale fluxurilor de lucru](/l/ro/user-guide/workflows/capabilities/workflow-actions)
* [Trimiteți e-mailuri din fluxuri de lucru](/l/ro/user-guide/workflows/capabilities/send-emails-from-workflows)
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Аутентификация
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Назначение |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Заметки:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: Часто задаваемые вопросы об ИИ
description: Часто задаваемые вопросы о функциях ИИ в Twenty.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="Когда функции ИИ будут доступны?">
Функции ИИ сейчас находятся в разработке и вскоре будут выпущены в бета-версии. Следите за обновлениями!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="Какие возможности ИИ планируются?">
Мы разрабатываем две основные возможности ИИ:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **ИИ-чат-бот**: контекстный помощник, который может получать доступ к вашим данным в Twenty и помогать с запросами
2. **ИИ-агенты в рабочих процессах**: интеллектуальная автоматизация, которая может обрабатывать данные, принимать решения и выполнять задачи в рамках ваших рабочих процессов
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="Будут ли ИИ-агенты иметь доступ ко всем моим данным?">
ИИ-агенты будут работать в рамках системы разрешений. Вы можете назначать ИИ-агентам конкретные роли в **Настройки → Роли**, получая полный контроль над тем, к каким данным у них есть доступ и какие действия они могут выполнять.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="Как будут работать кредиты ИИ?">
Действия ИИ будут расходовать кредиты рабочих процессов в зависимости от сложности задачи и используемой модели ИИ. Дополнительные сведения будут доступны при запуске функций.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="Могу ли я использовать собственные модели ИИ?">
Изначально Twenty будет использовать встроенные модели ИИ. Поддержка пользовательских или внешних моделей ИИ может быть добавлена в будущих релизах на основе отзывов пользователей.
</Accordion>
</AccordionGroup>
@@ -179,23 +179,23 @@ Twenty автоматически пытается сопоставить ваш
Вы можете оставить домен пустым. Однако мы рекомендуем по возможности добавлять домены для улучшения качества данных и автоматической привязки писем.
</Accordion>
<Accordion title="Can I import companies without any People linked?">
Да! You can import companies first, then import People later and link them using the company domain.
<Accordion title="Могу ли я импортировать компании без связанных контактов?">
Да! Сначала вы можете импортировать компании, а позже импортировать контакты и связать их с помощью домена компании.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="Что произойдет, если я импортирую домен, который уже существует?">
Если вы укажете уникальный идентификатор (домен или id), который совпадает с существующей компанией, Twenty обновит эту компанию вместо создания дубликата.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="Как исправить ошибки 'duplicate domain'?">
Либо удалите дубликат из вашего файла, либо добавьте `id` компании, чтобы вместо этого обновить существующую запись.
</Accordion>
</AccordionGroup>
## Устранение неполадок
Having issues? Check:
Возникли проблемы? Проверьте:
* [How to Fix Import Errors](/l/ru/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/ru/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/ru/user-guide/data-migration/capabilities/uniqueness-constraints)
* [Как исправить ошибки импорта](/l/ru/user-guide/data-migration/how-tos/fix-import-errors)
* [Справочник по сопоставлению полей](/l/ru/user-guide/data-migration/capabilities/field-mapping)
* [Ограничения уникальности](/l/ru/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: Поля связи
description: Connect records across different objects using relation fields.
description: Связывайте записи между разными объектами с помощью полей связи.
---
## Types of Relations
## Типы связей
### One-to-Many
### Один-ко-многим
One record in Object A can be linked to many records in Object B.
Одна запись в объекте A может быть связана со многими записями в объекте B.
**Example:** One Company can have many People (employees).
**Пример:** Одна Компания может иметь много Людей (сотрудников).
### Many-to-One
### Многие-к-одному
Many records in Object A can be linked to one record in Object B.
Много записей в объекте A могут быть связаны с одной записью в объекте B.
**Example:** Many People can belong to one Company.
**Пример:** Многие Люди могут принадлежать одной Компании.
### Relations to Multiple Object Types
### Связи с несколькими типами объектов
Some objects can link to multiple object types on one side of the relation.
Некоторые объекты могут быть связаны с несколькими типами объектов на одной стороне связи.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**Пример:** Заметка может одновременно быть прикреплена к одному Человеку И одной Компании И одной Сделке. Заметка — на стороне «многие», связываясь с несколькими сторонами «один».
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
Аналогично, Проект (на стороне «один») может получать связи от нескольких Людей, нескольких Компаний и нескольких Заметок.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**Ограничение импорта/экспорта**: Связи, указывающие на несколько типов объектов, пока не поддерживаются для импорта/экспорта CSV. Это запланировано в нашей дорожной карте.
</Warning>
### Many-to-Many
### Многие-ко-многим
Many records in Object A can be linked to many records in Object B.
Много записей в объекте A могут быть связаны с множеством записей в объекте B.
**Example:** Many People can be linked to many Projects, and vice versa.
**Пример:** Многие Люди могут быть связаны со многими Проектами, и наоборот.
<Warning>
**Many-to-Many is not yet supported.**
**Связи многие-ко-многим пока не поддерживаются.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
Этот тип связи запланирован на первое полугодие 2026 года. В качестве обходного решения создайте промежуточный "соединительный" объект (например, "Назначения проектов"), который имеет связи типа многие-к-одному с обоими объектами.
</Warning>
## Creating a Relation Field
## Создание поля связи
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. Перейдите в **Настройки → Модель данных**
2. Выберите объект, в который вы хотите добавить связь
3. Нажмите **+ Добавить поле**
4. Выберите **Связь** как тип поля
5. Выберите целевые объекты, с которыми нужно установить связь
6. Настройте параметры связи:
* **Имя поля на исходном объекте**: Название поля связи на объекте, который вы редактируете
* **Имя поля на целевом объекте**: Название поля связи, которое будет отображаться на целевом объекте
* Тип связи (один-ко-многим, многие-к-одному)
7. Нажмите **Сохранить**
## Standard Relations
## Стандартные связи
Twenty comes with pre-built relations between standard objects:
В Twenty есть предустановленные связи между стандартными объектами:
| From Object | To Object | Relation Type |
| ----------- | --------- | ------------- |
| Люди | Компании | Many-to-One |
| Возможности | Компании | Many-to-One |
| Возможности | Люди | Many-to-One |
| Исходный объект | Целевой объект | Тип связи |
| --------------- | -------------- | --------------- |
| Люди | Компании | Многие-к-одному |
| Возможности | Компании | Многие-к-одному |
| Возможности | Люди | Многие-к-одному |
## Лучшие практики
### Planning Relations
### Планирование связей
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **Составьте карту модели данных**: Планируйте связи перед их созданием
* **Учитывайте направление**: Подумайте, какой объект «владеет» связью
* **Избегайте циклических зависимостей**: Поддерживайте модель данных в чистоте
### Naming Relations
### Именование связей
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **Используйте понятные названия**: Сделайте так, чтобы было ясно, что представляет собой связь
* **Соблюдайте последовательность**: Используйте единые шаблоны именования для всех связей
* **Учитывайте обе стороны**: Корректно назовите обе стороны связи
### Performance
### Производительность
* **Don't over-relate**: Too many relations can slow down your workspace
* **Не злоупотребляйте связями**: Слишком много связей может замедлить ваше рабочее пространство
## Limitations
## Ограничения
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **Удаление связей** удаляет связи, но не связанные записи
* **Циклических связей** следует избегать для обеспечения целостности данных
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: Создание пользовательских полей
description: Пошаговое руководство по добавлению пользовательских полей в любой объект.
---
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
Пользовательские поля позволяют собирать информацию, характерную для вашего бизнеса. Добавляйте их в любой объект — стандартный или пользовательский.
## Steps
## Шаги
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/ru/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. Перейдите в **Настройки → Модель данных**
2. Выберите объект, в который вы хотите добавить поле
3. Нажмите **+ Добавить поле**
4. Выберите **тип поля** (все типы см. в разделе [Поля](/l/ru/user-guide/data-model/capabilities/fields))
5. Введите **имя поля** и при необходимости описание
6. Настройте параметры, специфичные для поля (см. ниже)
7. Нажмите **Сохранить**
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**Быстрый способ:** нажмите **+** в конце заголовков столбцов в любом табличном представлении → **Настроить поля**.
## Show the Field in Views
## Отображение поля в представлениях
New fields aren't automatically visible. To display:
Новые поля не отображаются автоматически. Чтобы отобразить:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. Откройте табличное представление объекта
2. Нажмите **Параметры → Поля**
3. Нажмите **значок глаза** рядом с вашим полем, чтобы отобразить его
4. Перетащите, чтобы изменить порядок
## Configuration Options
## Параметры настройки
### For Select / Multi-Select
### Для полей «Выбор» / «Множественный выбор»
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. Нажмите **+ Добавить вариант**, чтобы создать варианты выбора
2. При необходимости задайте **вариант по умолчанию**
3. Перетащите, чтобы изменить порядок вариантов
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/ru/user-guide/data-migration/capabilities/field-mapping).
**Используйте имена API для импорта.** Включите **Расширенный режим** в Настройках, чтобы видеть имена API. См. [Сопоставление полей](/l/ru/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### Для валютных полей
Set the **default currency** (USD, EUR, etc.) for new records.
Установите **валюту по умолчанию** (USD, EUR и т. д.) для новых записей.
### For Phone Fields
### Для телефонных полей
Set the **default country code** to pre-fill for new phone numbers.
Установите **код страны по умолчанию** для автозаполнения новых телефонных номеров.
### Making a Field Unique
### Уникальность поля
Toggle **Unique** to prevent duplicate values across records.
Включите **Уникальность**, чтобы предотвратить дубли значений между записями.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
Если уже есть дубликаты (включая удалённые записи), вы получите ошибку. Сначала устраните дубликаты.
</Note>
### Setting Default Values
### Установка значений по умолчанию
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
Для полей «Выбор» вы можете указать, какой вариант будет предварительно выбран для новых записей. Для полей «Флажок» задайте, будет ли он установлен или снят по умолчанию.
## Deactivating a Field
## Деактивация поля
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. Перейдите в **Настройки → Модель данных**
2. Найдите поле
3. Нажмите **⋮ → Деактивировать**
Data is preserved. You can reactivate or permanently delete later.
Данные сохраняются. Позже вы сможете повторно активировать или удалить навсегда.
## Related
## Связанные материалы
* [Fields](/l/ru/user-guide/data-model/capabilities/fields) — all field types explained
* [Поля](/l/ru/user-guide/data-model/capabilities/fields) — описание всех типов полей
* [Частые вопросы о модели данных](/l/ru/user-guide/data-model/how-tos/data-model-faq) — распространённые вопросы
@@ -1,6 +1,6 @@
---
title: Модель данных
description: Learn what a data model is and how to design one that fits your business.
description: Узнайте, что такое модель данных и как спроектировать её под ваш бизнес.
image: /images/user-guide/fields/custom_data_model.png
---
@@ -8,120 +8,120 @@ image: /images/user-guide/fields/custom_data_model.png
<img src="/images/user-guide/fields/custom_data_model.png" alt="Модель данных" />
</Frame>
## What is a Data Model?
## Что такое модель данных?
Модель данных — это структура, определяющая, как информация организована в вашей CRM. Think of it as the **blueprint** of your customer data — you design it once, then fill it with your actual data.
Модель данных — это структура, определяющая, как информация организована в вашей CRM. Представьте это как **чертёж** ваших данных о клиентах — вы проектируете его один раз, а затем заполняете фактическими данными.
## Key Concepts
## Ключевые понятия
### Объекты
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
**Объекты** — это основные категории данных в вашей CRM. Каждый объект представляет тип сущности, которую вы хотите отслеживать.
Twenty comes with standard objects:
В Twenty предусмотрены стандартные объекты:
* **People** — individuals (contacts, leads, partners)
* **Companies** — organizations
* **Opportunities** — deals or sales
* **Notes** — attached notes on records
* **Tasks** — to-dos linked to records
* **People** — физические лица (контакты, лиды, партнёры)
* **Companies** — организации
* **Opportunities** — сделки или продажи
* **Notes** — прикреплённые к записям заметки
* **Tasks** — задачи, связанные с записями
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
Вы также можете создать **пользовательские объекты** для всего, что специфично для вашего бизнеса (например, Projects, Subscriptions, Events).
### Поля
**Fields** are the properties or attributes that describe each object. They store the actual information.
**Поля** — это свойства или атрибуты, которые описывают каждый объект. Они хранят фактическую информацию.
For example, the **People** object has fields like:
Например, у объекта **People** есть такие поля, как:
* Имя
* Электронная почта
* Телефон
* Должность
* Company (a relation to the Companies object)
* Company (связь с объектом Companies)
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
Поля имеют разные **типы**: текст, число, дата, список, множественный выбор, связь и другие. Вы можете добавлять пользовательские поля к любому объекту.
### Записи
**Records** are the individual entries within an object — the actual data you create and manage.
**Записи** — это отдельные элементы внутри объекта — реальные данные, которые вы создаёте и которыми управляете.
Например:
* "John Smith" is a **record** in the People object
* "Acme Corp" is a **record** in the Companies object
* "John Smith" — это **запись** в объекте People
* "Acme Corp" — это **запись** в объекте Companies
**An analogy:**
**Аналогия:**
| Data Model Concept | Real-World Analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **Поля** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
| Концепция модели данных | Аналогия из реального мира |
| ----------------------- | ---------------------------------------------- |
| **Объекты** | Разделы в книге (категории) |
| **Поля** | Столбцы в электронной таблице (свойства) |
| **Записи** | Строки в электронной таблице (реальные записи) |
You design the data model (objects + fields) once, then create many records within that structure.
Вы проектируете модель данных (объекты + поля) один раз, затем создаёте множество записей в рамках этой структуры.
## Why Customize Your Data Model?
## Зачем настраивать вашу модель данных?
Каждый бизнес работает по-своему. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
Каждый бизнес работает по-своему. Настройка вашей модели данных означает, что вы можете адаптировать Twenty под **ваши** процессы, а не загонять их в жёсткую систему.
Twenty offers full flexibility:
Twenty предоставляет полную гибкость:
* Create as many custom objects as you need
* Add unlimited custom fields
* The price doesn't change based on customization
* Создавайте столько пользовательских объектов, сколько вам нужно
* Добавляйте неограниченное количество пользовательских полей
* Цена не меняется в зависимости от настроек
## Tips to Design Your Data Model
## Советы по проектированию вашей модели данных
### 1. Start with Your Core Objects
### 1. Начните с основных объектов
Identify the main concepts you work with. Twenty already provides:
Определите основные сущности, с которыми вы работаете. В Twenty уже предусмотрено:
* **People** — your contacts
* **Companies** — your accounts
* **Opportunities** — your deals
* **People** — ваши контакты
* **Companies** — ваши компании
* **Opportunities** — ваши сделки
Think about what else you might need:
Подумайте, что ещё может понадобиться:
* Stripe would need a `Subscriptions` object
* Airbnb would need a `Trips` object
* An accelerator would need a `Batches` object
* Stripe нужен объект `Subscriptions`
* Airbnb нужен объект `Trips`
* Акселератору нужен объект `Batches`
### 2. Use Fields for Variations, Not New Objects
### 2. Используйте поля для вариаций, а не новые объекты
If something is just a characteristic of an existing object, make it a **field**.
Если что-то является лишь характеристикой существующего объекта, сделайте это **полем**.
**Use fields for:**
**Используйте поля для:**
* Categories and labels (e.g., `Industry` for Companies)
* Status values (e.g., `Stage` for Opportunities)
* Attributes and properties
* Категорий и меток (например, `Industry` для Companies)
* Значений статуса (например, `Stage` для Opportunities)
* Атрибутов и свойств
### 3. Create an Object When It Stands on Its Own
### 3. Создавайте объект, если он существует сам по себе
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
Если у концепции есть свой жизненный цикл, свойства или отношения, она заслуживает отдельного объекта.
**Create an object for:**
**Создавайте объект для:**
* **Projects** — have deadlines, owners, and tasks
* **Subscriptions** — connect companies, products, and invoices
* **Events** — involve attendees and follow-up actions
* **Projects** — со сроками, владельцами и задачами
* **Subscriptions** — связывают компании, продукты и счета
* **Events** — включают участников и последующие действия
Они выходят за рамки одного поля, поскольку несут свои данные и связи.
### 4. Create an Object When Records Are Open-Ended
### 4. Создавайте объект, когда число записей не ограничено
If something can be linked multiple times and you don't know how many, use an object.
Если что-то может связываться многократно, и вы не знаете, сколько раз, используйте объект.
**Bad approach:**
Creating fields like `Product 1`, `Product 2`, `Product 3`...
**Плохой подход:**
Создание полей вроде `Product 1`, `Product 2`, `Product 3`...
**Good approach:**
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
**Хороший подход:**
Создайте объект `Products` и свяжите его с записями. Таким образом, вы можете поддерживать один, два или сто товаров без изменения вашей модели.
### 5. Keep It Simple First
### 5. Сначала — простота
Start with fields. Move to new objects only when you feel the limits:
Начните с полей. Move to new objects only when you feel the limits:
* Too many fields on one object
* Repeated records that should be separate
@@ -3,7 +3,7 @@ title: Настройки рабочей области
description: Настройте название и брендинг вашей рабочей области.
---
Those are accessible under **Settings → General**.
Они доступны в разделе **Настройки → Общие**.
## Изображение рабочей области
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: Поля и столбцы
description: Выберите, какие поля отображать, и как их упорядочить.
---
## Selecting Fields to Display
## Выбор полей для отображения
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
Каждое представление может отображать разный набор полей. Настройте, что отображается, чтобы сосредоточиться на важной информации.
### Show or Hide Fields
### Показ или скрытие полей
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. Нажмите **Параметры** в правом верхнем углу
2. Нажмите **Поля**
3. Нажмите **значок глаза** рядом с каждым полем, чтобы показать/скрыть его
### Reorder Fields
### Изменение порядка полей
Change the order fields appear in your view:
Измените порядок отображения полей в вашем представлении:
1. Click **Options → Fields**
2. Drag fields up or down
1. Нажмите **Параметры → Поля**
2. Перетаскивайте поля вверх или вниз
3. Изменения сохраняются автоматически
## Field Display by View Type
## Отображение полей по типу представления
### Table Views
### Табличные представления
* Fields appear as columns
* Resize columns by dragging borders
* Поля отображаются в виде столбцов
* Изменяйте размер столбцов, перетаскивая границы
### Kanban Views
### Канбан-представления
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* Поля отображаются на карточках
* Измените порядок через Параметры → Поля
* Используйте Компактный вид, чтобы скрыть все поля
### Calendar Views
### Календарные представления
* Selected fields show on calendar events
* Configure via Options → Fields
* Выбранные поля отображаются в событиях календаря
* Настройте через Параметры → Поля
## Лучшие практики
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **Показывайте только нужное** — слишком много полей перегружает представление
* **Размещайте важные поля первыми** — самые часто используемые столбцы слева
* **Создавайте несколько представлений** — разные наборы полей для разных задач
* **Используйте видимость полей для каждого представления** — один и тот же объект, разные акценты
## Related
## Связанные материалы
* [Table Views](/l/ru/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/ru/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [Табличные представления](/l/ru/user-guide/views-pipelines/capabilities/table-views) — возможности табличных представлений
* [Канбан-представления](/l/ru/user-guide/views-pipelines/capabilities/kanban-views) — карточные представления
@@ -1,38 +1,38 @@
---
title: Kanban Board Views
description: Learn how to use Kanban views to visualize and manage your workflows.
title: Представления канбан-доски
description: Узнайте, как использовать канбан-представления для визуализации и управления вашими рабочими процессами.
image: /images/user-guide/kanban-views/kanban.png
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## About Kanban Views
## О канбан-представлениях
Kanban views visually map out process flows, where each column stands for a distinct stage and each card represents a record.
Канбан-представления наглядно отображают потоки процессов: каждый столбец соответствует отдельному этапу, а каждая карточка представляет запись.
## Move Cards between Stages
## Перемещайте карточки между этапами
Каждая карта может перемещаться по мере прохождения рабочего процесса путем перетаскивания. Для продолжения удерживайте нажатие на карточке и переместите её в следующую стадию.
<VimeoEmbed videoId="927888627" title="Видеодемонстрация" />
## Add and Delete Stages
## Добавление и удаление этапов
Вы можете настроить ваш рабочий процесс в соответствии с вашими нуждами, используя стадии, которые представляют значение в поле выбора:
### Добавить стадии
To add a stage, access the Select field settings by navigating to Settings > Data Model, selecting your object, and then the field your Kanban board depends on.
Чтобы добавить этап, откройте настройки поля выбора, перейдя в Настройки > Модель данных, выберите объект, а затем поле, от которого зависит ваша канбан-доска.
<VimeoEmbed videoId="927890428" title="Видеодемонстрация" />
### Удалить стадии
To remove a stage, hover the stage name or the `⋮` icon, click `Edit from settings` in the Select field settings, and then click **Delete** next to the relevant stage.
Чтобы удалить этап, наведите курсор на название этапа или значок `⋮`, щёлкните `Редактировать в настройках` в настройках поля выбора, затем нажмите **Удалить** рядом с соответствующим этапом.
## Display Fields
## Отображаемые поля
Вы можете настроить доску Канбан для отображения некоторых полей и скрытия других. To hide a field, click on **Options** on the top right, then on **Fields** to bring up the list of options. Look for the field needed in the Hidden Fields section and click on the eye button to display the field.
Вы можете настроить доску Канбан для отображения некоторых полей и скрытия других. Чтобы скрыть поле, нажмите **Параметры** в правом верхнем углу, затем нажмите **Поля**, чтобы открыть список параметров. Найдите нужное поле в разделе Скрытые поля и нажмите на значок в виде глаза, чтобы отобразить поле.
Вы также можете изменить порядок полей, удерживая имя поля и перетаскивая его туда, где хотите видеть.
@@ -40,60 +40,60 @@ To remove a stage, hover the stage name or the `⋮` icon, click `Edit from sett
## Компактный Вид
You can hide all the fields and get an overview of all records at a glance. To enable:
Вы можете скрыть все поля и получить обзор всех записей одним взглядом. Чтобы включить:
1. Click **Options** on the top right
2. Turn on the toggle for **Compact view**
1. Нажмите **Параметры** в правом верхнем углу
2. Включите переключатель **Компактный вид**
<img src="/images/user-guide/kanban-views/compact-view.png" style={{width:'100%'}} />
## Column Aggregations
## Агрегирование в столбцах
Each column in a Kanban view can display aggregated values at the top, helping you understand your data at a glance.
Каждый столбец в канбан-представлении может отображать вверху агрегированные значения, что помогает быстро оценить данные.
### Available Aggregations
### Доступные агрегирования
| Aggregation | Описание |
| ----------- | --------------------------------------------- |
| **Count** | Number of records in the column |
| **Sum** | Total of a numeric field (e.g., deal amounts) |
| **Average** | Average value of a numeric field |
| **Min** | Lowest value |
| **Max** | Highest value |
| Агрегирование | Описание |
| -------------- | ------------------------------------------------------ |
| **Количество** | Количество записей в столбце |
| **Сумма** | Сумма значений числового поля (например, суммы сделок) |
| **Среднее** | Среднее значение числового поля |
| **Минимум** | Минимальное значение |
| **Максимум** | Максимальное значение |
### Configuring Aggregations
### Настройка агрегирования
1. Click on the number displayed next to the Stage value, at the top of a column
2. Select the aggregation type
3. Choose the field to aggregate
1. Щёлкните по числу, отображаемому рядом со значением этапа вверху столбца
2. Выберите тип агрегирования
3. Выберите поле для агрегирования
**Example:** Show total deal value per stage by aggregating the Amount field with Sum.
**Пример:** Показать общую стоимость сделок по этапам, агрегируя поле Amount с помощью Sum.
## When to Use Kanban Views
## Когда использовать канбан-представления
Kanban views are ideal for:
Канбан-представления лучше всего подходят для:
* **Sales pipelines**: Track deals through stages from lead to close
* **Project management**: Monitor tasks through workflow states
* **Recruitment**: Track candidates through hiring stages
* **Any staged process**: Visualize any workflow with defined stages
* **Воронки продаж**: Отслеживайте сделки по этапам — от лида до закрытия
* **Управление проектами**: Отслеживайте задачи по состояниям рабочего процесса
* **Подбор персонала**: Отслеживайте кандидатов по этапам найма
* **Любой поэтапный процесс**: Визуализируйте любой рабочий процесс с определёнными этапами
## Лучшие практики
### Organize Your Stages
### Организуйте этапы
* **Limit stages**: 5-7 stages is ideal for visibility
* **Clear naming**: Use descriptive stage names
* **Logical order**: Arrange stages in process order
* **Ограничьте число этапов**: 5-7 этапов — оптимально для наглядности
* **Понятные названия**: Используйте описательные названия этапов
* **Логичный порядок**: Расположите этапы в порядке процесса
### Optimize Card Display
### Оптимизируйте отображение карточек
* **Show key fields**: Display only the most important information
* **Use compact view**: For high-level overviews
* **Color coding**: Use stage colors to quickly identify status
* **Показывайте ключевые поля**: Отображайте только самую важную информацию
* **Используйте компактный вид**: Для общего обзора
* **Цветовое кодирование**: Используйте цвета этапов для быстрого определения статуса
### Maintain Data Quality
### Поддерживайте качество данных
* **Update regularly**: Keep cards moving through stages
* **Archive completed**: Move closed items out of active view
* **Review stale cards**: Follow up on cards stuck in stages
* **Регулярно обновляйте**: Продвигайте карточки по этапам
* **Архивируйте завершённые**: Перемещайте закрытые элементы из активного представления
* **Проверяйте «застаивающиеся» карточки**: Отслеживайте карточки, застрявшие на этапах
@@ -1,140 +1,140 @@
---
title: Closed Won Automations
description: Automate post-win activities when opportunities close.
title: Автоматизации для стадии «Закрыта — выиграна»
description: Автоматизируйте действия после выигрыша, когда возможность закрывается.
---
When a deal closes, multiple things need to happen: update company status, notify team members, create onboarding tasks. Automate all of this with a single workflow.
Когда сделка закрывается, нужно сделать несколько вещей: обновить статус компании, уведомить участников команды, создать задачи по онбордингу. Автоматизируйте всё это одним рабочим процессом.
## The Problem
## Проблема
When an opportunity moves to "Closed Won":
Когда возможность переходит в стадию «Закрыта — выиграна»:
* Company type needs to change from "Prospect" to "Customer"
* Onboarding tasks need to be created
* Customer success team needs to be notified
* Sales rep needs confirmation
* Тип компании должен измениться с «Потенциальный клиент» на «Клиент»
* Нужно создать задачи по онбордингу
* Команду Customer Success необходимо уведомить
* Менеджеру по продажам нужно подтверждение
Doing this manually is time-consuming and error-prone.
Вручную это занимает много времени и приводит к ошибкам.
## The Solution
## Решение
Create a workflow that handles all post-win activities automatically.
Создайте рабочий процесс, который автоматически выполнит все действия после выигрыша.
## Complete Workflow Setup
## Полная настройка рабочего процесса
### Step 1: Create the Workflow
### Шаг 1: Создайте рабочий процесс
1. Go to **Settings → Workflows**
2. Click **+ New Workflow**
3. Name it "Deal Won - Post-Win Automation"
1. Перейдите в **Настройки → Рабочие процессы**
2. Нажмите **+ Новый рабочий процесс**
3. Назовите его "Deal Won - Post-Win Automation"
### Step 2: Configure the Trigger
### Шаг 2: Настройте триггер
1. Select **Record is Updated**
2. Choose **Opportunities**
3. Under "Fields to monitor", select **Stage**
1. Выберите **Запись обновлена**
2. Выберите **Возможности**
3. В разделе "Поля для отслеживания" выберите **Стадия**
### Step 3: Add Stage Filter
### Шаг 3: Добавьте фильтр по стадии
1. Add **Filter** action
2. Condition: `{{trigger.object.stage}}` equals "Closed Won"
1. Добавьте действие **Фильтр**
2. Условие: `{{trigger.object.stage}}` равно «Закрыта выиграна»
### Step 4: Update Company Type
### Шаг 4: Обновите тип компании
1. Add **Update Record** action
1. Добавьте действие **Обновить запись**
2. Настроить:
| Поле | Значение |
| --------------------- | ------------------------------- |
| **Object** | Компании |
| **Record** | `{{trigger.object.company.id}}` |
| **Тип** | Клиент |
| **First Deal Date** | `{{trigger.object.closedAt}}` |
| **Владелец аккаунта** | `{{trigger.object.owner.id}}` |
| Поле | Значение |
| ---------------------- | ------------------------------- |
| **Объект** | Компании |
| **Запись** | `{{trigger.object.company.id}}` |
| **Тип** | Клиент |
| **Дата первой сделки** | `{{trigger.object.closedAt}}` |
| **Владелец аккаунта** | `{{trigger.object.owner.id}}` |
### Step 5: Create Onboarding Task
### Шаг 5: Создайте задачу по онбордингу
1. Add **Create Record** action
1. Добавьте действие **Создать запись**
2. Настроить:
| Поле | Значение |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Object** | Задачи |
| **Title** | `Onboarding: {{trigger.object.name}}` |
| **Assignee** | Customer Success team member |
| **Due Date** | 3 days from now |
| **Priority** | High |
| **Related Company** | `{{trigger.object.company.id}}` |
| **Related Opportunity** | `{{trigger.object.id}}` |
| **Description** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
| Поле | Значение |
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Объект** | Задачи |
| **Заголовок** | `Onboarding: {{trigger.object.name}}` |
| **Исполнитель** | Сотрудник команды Customer Success |
| **Срок исполнения** | Через 3 дня |
| **Приоритет** | Высокий |
| **Связанная компания** | `{{trigger.object.company.id}}` |
| **Связанная возможность** | `{{trigger.object.id}}` |
| **Описание** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
### Step 6: Notify Customer Success
### Шаг 6: Уведомите команду Customer Success
1. Add **Send Email** action
1. Добавьте действие **Send Email**
2. Настроить:
| Поле | Значение |
| ----------- | -------------------------------------------------- |
| **To** | customer-success@yourcompany.com |
| **Subject** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Body** | See example below |
| Поле | Значение |
| --------- | -------------------------------------------------- |
| **Кому** | customer-success@yourcompany.com |
| **Тема** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Текст** | См. пример ниже |
**Email body example**:
**Пример текста письма**:
```
Hi CS Team,
Привет, команда Customer Success,
We have a new customer!
У нас новый клиент!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
Компания: {{trigger.object.company.name}}
Сделка: {{trigger.object.name}}
Сумма: {{trigger.object.amount}}
Менеджер по продажам: {{trigger.object.owner.name}}
Дата закрытия: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Задача по онбордингу создана автоматически.
Let's give them a great start!
Давайте обеспечим им отличный старт!
```
### Step 7: Confirm to Sales Rep
### Шаг 7: Отправьте подтверждение менеджеру по продажам
1. Add another **Send Email** action
1. Добавьте ещё одно действие **Send Email**
2. Настроить:
| Поле | Значение |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| **To** | `{{trigger.object.owner.email}}` |
| **Subject** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Body** | Congratulations! Your deal has been processed. The customer success team has been notified and onboarding has begun. |
| Поле | Значение |
| --------- | ------------------------------------------------------------------------------------------ |
| **Кому** | `{{trigger.object.owner.email}}` |
| **Тема** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Текст** | Поздравляем! Ваша сделка обработана. Команда Customer Success уведомлена, онбординг начат. |
### Step 8: Test and Activate
### Шаг 8: Протестируйте и активируйте
1. Test by moving a test opportunity to "Closed Won"
1. Протестируйте, переместив тестовую возможность в стадию «Закрыта выиграна»
2. Проверить:
* Company type changed to "Customer"
* Onboarding task created
* CS team received email
* Sales rep received confirmation
3. Activate when ready
* Тип компании изменился на «Клиент»
* Задача по онбордингу создана
* Команда Customer Success получила письмо
* Менеджер по продажам получил подтверждение
3. Активируйте, когда будете готовы
## Handling Closed Lost
## Обработка статуса «Закрыта проиграна»
Create a similar workflow for lost deals:
Создайте аналогичный рабочий процесс для проигранных сделок:
### Триггер
* Record is Updated (Opportunities, Stage = "Closed Lost")
* Запись обновлена (Возможности, стадия = «Закрыта проиграна»)
### Действия
1. **Create Record**: Task for "Lost Deal Analysis"
2. **Update Record**: Add lost reason to company record
3. **Send Email**: Notify manager of lost deal
1. **Создать запись**: Задача «Анализ проигранной сделки»
2. **Обновить запись**: Добавьте причину проигрыша в запись компании
3. **Send Email**: Уведомите менеджера о проигранной сделке
## Advanced: Multi-Step Onboarding
## Продвинутое: многошаговый онбординг
For complex onboarding, create multiple tasks:
Для сложного онбординга создайте несколько задач:
```javascript
export const main = async (params) => {
@@ -149,31 +149,31 @@ export const main = async (params) => {
};
```
Use **Iterator** to create each task from the array.
Используйте **Iterator** для создания каждой задачи из массива.
## Customization Ideas
## Идеи для настройки
### Keep your other tools up-to-date
### Держите другие инструменты в актуальном состоянии
* Create customer in billing system with an **HTTP Request**
* Создайте клиента в биллинговой системе с помощью **HTTP Request**
### Conditional Actions
### Условные действия
Use **Filter** actions to:
Используйте действия **Фильтр**, чтобы:
* Different onboarding for enterprise vs SMB
* Different assignees based on region
* Skip notifications for small deals
* Разный онбординг для Enterprise и SMB
* Разные исполнители в зависимости от региона
* Пропускать уведомления для небольших сделок
### Include Deal Details
### Включите детали сделки
Use **Code** action to format:
Используйте действие **Code** для форматирования:
* Deal summary documents
* Handoff notes for CS team
* Custom onboarding checklists
* Документы с краткой сводкой по сделке
* Заметки для передачи команде Customer Success
* Пользовательские контрольные списки онбординга
## Related
## Связанные материалы
* [Workflow Actions](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Send Emails from Workflows](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Отправка писем из рабочих процессов](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)
@@ -92,9 +92,59 @@ my-twenty-app/
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
### Convention-over-configuration
Applications use a **convention-over-configuration** approach where entities are detected by their file suffix. This allows flexible organization within the `src/app/` folder:
| File suffix | Entity type |
| --------------- | ------------------------------- |
| `*.object.ts` | Custom object definitions |
| `*.function.ts` | Serverless function definitions |
| `*.role.ts` | Role definitions |
### Supported folder organizations
You can organize your entities in any of these patterns:
**Traditional (by type):**
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
**Feature-based:**
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Flat:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
At a high level:
@@ -103,17 +153,19 @@ At a high level:
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See Application config below.
* `role.config.ts`: Default function role used by your serverless functions. See Default function role below.
* Future entities, actions/functions, and any supporting code you add.
* **src/app/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See "Application config" below.
* `*.role.ts`: Role definitions used by your serverless functions. See "Default function role" below.
* `*.object.ts`: Custom object definitions.
* `*.function.ts`: Serverless function definitions.
* **src/utils/**: Optional folder for handler implementations and utilities.
Later commands will add more files and folders:
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
* `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
## Kimlik Doğrulama
@@ -136,28 +188,28 @@ yarn auth --workspace my-custom-workspace
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
### Helper functions
The SDK provides four helper functions with built-in validation for defining your app entities:
| Function | Amaç |
| ------------------ | -------------------------------------------- |
| `defineApp()` | Configure application metadata |
| `defineObject()` | Define custom objects with fields |
| `defineFunction()` | Define serverless functions with handlers |
| `defineRole()` | Configure role permissions and object access |
These functions validate your configuration at runtime and provide better IDE autocompletion and type safety.
### Defining objects
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
Here is an example `postCard` object from the Hello World app:
Custom objects describe both schema and behavior for records in your workspace. Use `defineObject()` to define objects with built-in validation:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,84 +218,122 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Use `defineObject()` for built-in validation and better IDE support.
* The `universalIdentifier` must be unique and stable across deployments.
* Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
* The `fields` array is optional — you can define objects without custom fields.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
<Accordion title="Alternative: Decorator-based syntax">
You can also define objects using TypeScript decorators. This approach uses class-based syntax with `@Object`, `@Field`, and `@Relation` decorators:
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Note: The decorator approach requires `experimentalDecorators` in your TypeScript config.
</Accordion>
### Application config (application.config.ts)
@@ -253,89 +343,57 @@ Every app has a single `application.config.ts` file that describes:
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
When you scaffold a new app, you start with a minimal config:
Use `defineApp()` to define your application configuration:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `functionRoleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below).
#### Roles and permissions
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Applications can define roles that encapsulate permissions on your workspace's objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's serverless functions.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
##### Default function role (role.config.ts)
##### Default function role (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
When you scaffold a new app, the CLI also creates a default role file. Use `defineRole()` to define roles with built-in validation:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,10 +421,15 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
The `universalIdentifier` of this role is then referenced in `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **\*.role.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
Notes:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
@@ -376,20 +439,15 @@ Notes:
### Serverless function config and entrypoint
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
Each function file uses `defineFunction()` to export a configuration with a handler and optional triggers. Use the `*.function.ts` file suffix for automatic detection.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
@@ -410,14 +468,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,35 +484,40 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
},
],
};
});
```
Common trigger types:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
* **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
* **cron**: Runs your function on a schedule using a CRON expression.
* **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
Notes:
* The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
* You can mix multiple trigger types in a single function.
You can create new functions in two ways:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
* **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
* **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
@@ -473,13 +537,13 @@ The client is re-generated by `yarn generate`. Re-run after changing your object
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_KEY`: Shortlived key scoped to your application's default function role.
Notes:
Notlar:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* The API key's permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role's universal identifier.
### Hello World example
@@ -1,29 +1,29 @@
---
title: AI FAQ
description: Frequently asked questions about AI features in Twenty.
title: Yapay Zeka SSS
description: Twenty'deki yapay zeka özellikleri hakkında sıkça sorulan sorular.
---
<AccordionGroup>
<Accordion title="When will AI features be available?">
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
<Accordion title="Yapay zeka özellikleri ne zaman kullanıma sunulacak?">
Yapay zeka özellikleri şu anda geliştirme aşamasında ve yakında beta olarak yayınlanacak. Güncellemeler için takipte kalın!
</Accordion>
<Accordion title="What AI capabilities are planned?">
We're building two main AI capabilities:
<Accordion title="Hangi yapay zeka yetenekleri planlanıyor?">
İki ana yapay zeka yeteneği geliştiriyoruz:
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
1. **Yapay Zeka Sohbet Botu**: Twenty verilerinize erişebilen ve sorgularınızda size yardımcı olabilen bağlamın farkında olan bir asistan
2. **İş Akışlarında Yapay Zeka Ajanları**: İş akışlarınız içinde verileri işleyebilen, karar verebilen ve görevleri yerine getirebilen akıllı otomasyon
</Accordion>
<Accordion title="Will AI agents have access to all my data?">
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
<Accordion title="Yapay zeka ajanlarının tüm verilerime erişimi olacak mı?">
Yapay zeka ajanları izin sistemi kapsamında çalışacaktır. **Ayarlar → Roller** altında yapay zeka ajanlarına belirli roller atayarak hangi verilere erişebilecekleri ve hangi işlemleri gerçekleştirebilecekleri üzerinde tam kontrol sahibi olabilirsiniz.
</Accordion>
<Accordion title="How will AI credits work?">
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
<Accordion title="Yapay zeka kredileri nasıl çalışacak?">
Yapay zeka eylemleri, görevin karmaşıklığına ve kullanılan yapay zeka modeline bağlı olarak iş akışı kredilerini tüketecektir. Özellikler kullanıma sunulduğunda daha fazla ayrıntı mevcut olacak.
</Accordion>
<Accordion title="Can I use my own AI models?">
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
<Accordion title="Kendi yapay zeka modellerimi kullanabilir miyim?">
Başlangıçta, Twenty yerleşik yapay zeka modellerini kullanacaktır. Kullanıcı geri bildirimlerine bağlı olarak özel veya harici yapay zeka modellerine destek gelecekteki sürümlerde eklenebilir.
</Accordion>
</AccordionGroup>
@@ -179,23 +179,23 @@ Ayrıntılar için [Mevcut Kayıtlar Nasıl Güncellenir](/l/tr/user-guide/data-
Alan adını boş bırakabilirsiniz. Ancak, daha iyi veri kalitesi ve otomatik e-posta ilişkilendirmesi için mümkün olduğunda alan adlarını eklemenizi öneririz.
</Accordion>
<Accordion title="Can I import companies without any People linked?">
Evet! You can import companies first, then import People later and link them using the company domain.
<Accordion title="Kişilerle ilişkilendirilmeden şirketleri içe aktarabilir miyim?">
Evet! Önce şirketleri içe aktarabilir, ardından Kişileri içe aktarabilir ve şirketin etki alanını kullanarak bunları ilişkilendirebilirsiniz.
</Accordion>
<Accordion title="What happens if I import a domain that already exists?">
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
<Accordion title="Zaten var olan bir etki alanını içe aktarırsam ne olur?">
Mevcut bir şirketle eşleşen bir benzersiz tanımlayıcı (etki alanı veya id) dahil ederseniz, Twenty yinelenen bir kayıt oluşturmak yerine o şirketi günceller.
</Accordion>
<Accordion title="How do I fix 'duplicate domain' errors?">
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
<Accordion title="'duplicate domain' hatalarını nasıl düzeltirim?">
Dosyanızdan yineleneni kaldırın ya da mevcut kaydı güncellemek için şirketin `id` değerini ekleyin.
</Accordion>
</AccordionGroup>
## Sorun Giderme
Having issues? Check:
Sorun mu yaşıyorsunuz? Şunları kontrol edin:
* [How to Fix Import Errors](/l/tr/user-guide/data-migration/how-tos/fix-import-errors)
* [Field Mapping Reference](/l/tr/user-guide/data-migration/capabilities/field-mapping)
* [Uniqueness Constraints](/l/tr/user-guide/data-migration/capabilities/uniqueness-constraints)
* [İçe Aktarma Hataları Nasıl Düzeltilir](/l/tr/user-guide/data-migration/how-tos/fix-import-errors)
* [Alan Eşleme Başvurusu](/l/tr/user-guide/data-migration/capabilities/field-mapping)
* [Benzersizlik Kısıtlamaları](/l/tr/user-guide/data-migration/capabilities/uniqueness-constraints)
@@ -1,92 +1,92 @@
---
title: İlişki Alanları
description: Connect records across different objects using relation fields.
description: İlişki alanlarını kullanarak farklı nesnelerdeki kayıtları birbirine bağlayın.
---
## Types of Relations
## İlişki Türleri
### One-to-Many
### Bire-çok
One record in Object A can be linked to many records in Object B.
Nesne A'daki bir kayıt, Nesne B'deki birçok kayda bağlanabilir.
**Example:** One Company can have many People (employees).
**Örnek:** Bir Şirketin birçok Kişisi (çalışanı) olabilir.
### Many-to-One
### Çoktan-bire
Many records in Object A can be linked to one record in Object B.
Nesne A'daki birçok kayıt, Nesne B'deki tek bir kayda bağlanabilir.
**Example:** Many People can belong to one Company.
**Örnek:** Birçok Kişi tek bir Şirkete ait olabilir.
### Relations to Multiple Object Types
### Birden Fazla Nesne Türüne İşaret Eden İlişkiler
Some objects can link to multiple object types on one side of the relation.
Bazı nesneler, ilişkinin bir tarafında birden fazla nesne türüne bağlanabilir.
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
**Örnek:** Bir Not aynı anda bir Kişiye VE bir Şirkete VE bir Fırsata eklenebilir. Not "çok" tarafındadır ve birden çok "bir" tarafına bağlanır.
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
Benzer şekilde, bir Proje ("bir" tarafında) birden çok Kişiden, birden çok Şirketten ve birden çok Nottan bağlantılar alabilir.
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
<Warning>
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
**İçe/Dışa Aktarma kısıtlaması**: Birden fazla nesne türüne işaret eden ilişkiler CSV içe/dışa aktarma için henüz desteklenmiyor. Bu özellik yol haritamızda.
</Warning>
### Many-to-Many
### Çoktan-çoka
Many records in Object A can be linked to many records in Object B.
Nesne A'daki birçok kayıt, Nesne B'deki birçok kayda bağlanabilir.
**Example:** Many People can be linked to many Projects, and vice versa.
**Örnek:** Birçok Kişi birçok Projeye bağlanabilir ve tersi de geçerlidir.
<Warning>
**Many-to-Many is not yet supported.**
**Çoktan-çoka ilişkiler henüz desteklenmiyor.**
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
Bu ilişki türü 2026'nın ilk yarısı için planlanıyor. Geçici bir çözüm olarak, her iki nesneyle de Çoktan-bire ilişkileri olan aracı bir "junction" nesnesi (ör. "Proje Atamaları") oluşturun.
</Warning>
## Creating a Relation Field
## Bir İlişki Alanı Oluşturma
1. Go to **Settings → Data Model**
2. Select the object where you want to add the relation
3. Click **+ Add Field**
4. Select **Relation** as the field type
5. Choose the target object(s) to relate to
6. Configure the relation settings:
* **Field name on source object**: The name of the relation field on the object you're editing
* **Field name on destination object**: The name of the relation field that will appear on the target object
* Relation type (one-to-many, many-to-one)
1. **Ayarlar → Veri Modeli** bölümüne gidin
2. İlişki eklemek istediğiniz nesneyi seçin
3. **+ Alan Ekle**'ye tıklayın
4. Alan türü olarak **İlişki**'yi seçin
5. İlişkilendirilecek hedef nesne(leri) seçin
6. İlişki ayarlarını yapılandırın:
* **Kaynak nesnedeki alan adı**: Düzenlediğiniz nesnedeki ilişki alanının adı
* **Hedef nesnedeki alan adı**: Hedef nesnede görünecek ilişki alanının adı
* İlişki türü (bire-çok, çoktan-bire)
7. **Kaydet**'e tıklayın
## Standard Relations
## Standart İlişkiler
Twenty comes with pre-built relations between standard objects:
Twenty, standart nesneler arasında önceden oluşturulmuş ilişkilerle birlikte gelir:
| From Object | To Object | Relation Type |
| ----------- | --------- | ------------- |
| İnsanlar | Şirketler | Many-to-One |
| Fırsatlar | Şirketler | Many-to-One |
| Fırsatlar | İnsanlar | Many-to-One |
| Kaynak Nesne | Hedef Nesne | İlişki Türü |
| ------------ | ----------- | ----------- |
| İnsanlar | Şirketler | Çoktan-bire |
| Fırsatlar | Şirketler | Çoktan-bire |
| Fırsatlar | İnsanlar | Çoktan-bire |
## En İyi Uygulamalar
### Planning Relations
### İlişkileri Planlama
* **Map your data model**: Plan relations before creating them
* **Consider direction**: Think about which object "owns" the relationship
* **Avoid circular dependencies**: Keep your data model clean
* **Veri modelinizi haritalayın**: İlişkileri oluşturmadan önce planlayın
* **Yönü değerlendirin**: İlişkiye hangi nesnenin "sahip" olduğunu düşünün
* **Döngüsel bağımlılıklardan kaçının**: Veri modelinizi temiz tutun
### Naming Relations
### İlişkileri Adlandırma
* **Use clear names**: Make it obvious what the relation represents
* **Be consistent**: Use similar naming patterns across relations
* **Consider both sides**: Name both sides of the relation appropriately
* **Açık adlar kullanın**: İlişkinin neyi temsil ettiğini açıkça belirtin
* **Tutarlı olun**: İlişkiler genelinde benzer adlandırma kalıpları kullanın
* **Her iki tarafı da dikkate alın**: İlişkinin her iki tarafını da uygun şekilde adlandırın
### Performance
### Performans
* **Don't over-relate**: Too many relations can slow down your workspace
* **Aşırı ilişkilendirmeyin**: Çok fazla ilişki çalışma alanınızı yavaşlatabilir
## Limitations
## Kısıtlamalar
* **Deleting relations** removes the link but not the related records
* **Circular relations** should be avoided for data integrity
* **İlişkileri silmek** bağlantıyı kaldırır, ancak ilişkili kayıtları silmez
* **Döngüsel ilişkiler** veri bütünlüğünü korumak için kaçınılmalıdır
@@ -1,72 +1,72 @@
---
title: Create Custom Fields
description: Step-by-step guide to adding custom fields to any object.
title: Özel Alanlar Oluşturma
description: Herhangi bir nesneye özel alan eklemeye yönelik adım adım kılavuz.
---
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
Özel alanlar, işinize özgü bilgileri kaydetmenize olanak tanır. Standart ya da özel fark etmeksizin bunları herhangi bir nesneye ekleyin.
## Steps
## Adımlar
1. Go to **Settings → Data Model**
2. Select the object you want to add a field to
3. Click **+ Add Field**
4. Choose a **field type** (see [Fields](/l/tr/user-guide/data-model/capabilities/fields) for all types)
5. Enter the **field name** and optional description
6. Configure field-specific settings (see below)
1. **Ayarlar → Veri Modeli** bölümüne gidin
2. Alan eklemek istediğiniz nesneyi seçin
3. **+ Alan Ekle**'ye tıklayın
4. **alan türü** seçin (tüm türler için [Alanlar](/l/tr/user-guide/data-model/capabilities/fields) bölümüne bakın)
5. **alan adını** ve isteğe bağlı açıklamayı girin
6. Alana özel ayarları yapılandırın (aşağıya bakın)
7. **Kaydet**'e tıklayın
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
**Hızlı yöntem:** Herhangi bir tablo görünümünde sütun başlıklarının sonundaki **+** simgesine tıklayın → **Alanları özelleştir**.
## Show the Field in Views
## Alanı Görünümlerde Göster
New fields aren't automatically visible. To display:
Yeni alanlar otomatik olarak görünmez. Görüntülemek için:
1. Open the object's table view
2. Click **Options → Fields**
3. Click the **eye icon** next to your field to show it
4. Drag to reorder
1. Nesnenin tablo görünümünü açın
2. **Seçenekler → Alanlar**'a tıklayın
3. Alanınızın yanındaki **göz simgesine** tıklayarak gösterin
4. Yeniden sıralamak için sürükleyin
## Configuration Options
## Yapılandırma Seçenekleri
### For Select / Multi-Select
### Seçim / Çoklu Seçim için
1. Click **+ Add option** to create choices
2. Set a **default option** if desired
3. Drag to reorder options
1. Seçenekler oluşturmak için **+ Seçenek Ekle**'ye tıklayın
2. İsterseniz bir **varsayılan seçenek** belirleyin
3. Seçenekleri yeniden sıralamak için sürükleyin
<Note>
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/tr/user-guide/data-migration/capabilities/field-mapping).
**İçe aktarmalar için API adlarını kullanın.** API adlarını görmek için Ayarlar'da **Gelişmiş modu** etkinleştirin. Bkz. [Alan Eşleme](/l/tr/user-guide/data-migration/capabilities/field-mapping).
</Note>
### For Currency Fields
### Para Birimi Alanları için
Set the **default currency** (USD, EUR, etc.) for new records.
**varsayılan para birimini** ayarlayın (USD, EUR vb.) yeni kayıtlar için.
### For Phone Fields
### Telefon Alanları için
Set the **default country code** to pre-fill for new phone numbers.
Yeni telefon numaraları için önceden doldurulacak **varsayılan ülke kodunu** ayarlayın.
### Making a Field Unique
### Bir Alanı Benzersiz Yapma
Toggle **Unique** to prevent duplicate values across records.
Kayıtlar arasında yinelenen değerleri önlemek için **Benzersiz** seçeneğini etkinleştirin.
<Note>
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
Yinelenenler varsa (silinmiş kayıtlardakiler dahil) bir hata alırsınız. Önce yinelenenleri temizleyin.
</Note>
### Setting Default Values
### Varsayılan Değerleri Ayarlama
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
Seçim alanlarında yeni kayıtlar için önceden seçilecek seçeneği belirleyebilirsiniz. Onay kutusu alanlarında, varsayılan olarak işaretli mi işaretsiz mi olacağını ayarlayın.
## Deactivating a Field
## Bir Alanı Devre Dışı Bırakma
1. Go to **Settings → Data Model**
2. Find the field
3. Click **⋮ → Deactivate**
1. **Ayarlar → Veri Modeli** bölümüne gidin
2. Alanı bulun
3. **⋮ → Devre Dışı Bırak**'a tıklayın
Data is preserved. You can reactivate or permanently delete later.
Veriler korunur. Daha sonra yeniden etkinleştirebilir veya kalıcı olarak silebilirsiniz.
## Related
## İlgili
* [Fields](/l/tr/user-guide/data-model/capabilities/fields) — all field types explained
* [Data Model FAQ](/l/tr/user-guide/data-model/how-tos/data-model-faq) — common questions
* [Alanlar](/l/tr/user-guide/data-model/capabilities/fields) — tüm alan türleri açıklanır
* [Veri Modeli SSS](/l/tr/user-guide/data-model/how-tos/data-model-faq) — sık sorulan sorular
@@ -1,6 +1,6 @@
---
title: Veri modeli
description: Learn what a data model is and how to design one that fits your business.
description: Veri modelinin ne olduğunu ve işinize uygun bir modeli nasıl tasarlayacağınızı öğrenin.
image: /images/user-guide/fields/custom_data_model.png
---
@@ -8,120 +8,120 @@ image: /images/user-guide/fields/custom_data_model.png
<img src="/images/user-guide/fields/custom_data_model.png" alt="Veri modeli" />
</Frame>
## What is a Data Model?
## Veri modeli nedir?
Veri modeli, CRM'nizde bilgilerin nasıl organize edildiğini tanımlayan yapıdır. Think of it as the **blueprint** of your customer data — you design it once, then fill it with your actual data.
Veri modeli, CRM'nizde bilgilerin nasıl organize edildiğini tanımlayan yapıdır. Bunu müşteri verilerinizin **mimari planı** olarak düşünün — bir kez tasarlarsınız, sonra gerçek verilerinizle doldurursunuz.
## Key Concepts
## Temel Kavramlar
### Nesneler
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
**Objects**, CRM'inizdeki verilerin ana kategorileridir. Her nesne, izlemek istediğiniz bir şeyin türünü temsil eder.
Twenty comes with standard objects:
Twenty, standart nesnelerle birlikte gelir:
* **People** — individuals (contacts, leads, partners)
* **Companies** — organizations
* **Opportunities** — deals or sales
* **Notes** — attached notes on records
* **Tasks** — to-dos linked to records
* **People** — bireyler (kişiler, potansiyel müşteriler, iş ortakları)
* **Companies** — kuruluşlar
* **Opportunities** — anlaşmalar veya satışlar
* **Notes** — kayıtlara ekli notlar
* **Tasks** — kayıtlara bağlı yapılacaklar
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
İşinize özgü herhangi bir şey için **özel nesneler** de oluşturabilirsiniz (ör. Projects, Subscriptions, Events).
### Alanlar
**Fields** are the properties or attributes that describe each object. They store the actual information.
**Alanlar**, her nesneyi tanımlayan özellikler ya da özniteliklerdir. Gerçek bilgiyi depolarlar.
For example, the **People** object has fields like:
Örneğin, **People** nesnesinde şu gibi alanlar vardır:
* İsim
* E-posta
* Telefon
* İş Unvanı
* Company (a relation to the Companies object)
* Company (Companies nesnesine bir ilişki)
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
Alanların farklı **türleri** vardır: metin, sayı, tarih, seçim, çoklu seçim, ilişki ve daha fazlası. Her nesneye özel alanlar ekleyebilirsiniz.
### Kayıtlar
**Records** are the individual entries within an object — the actual data you create and manage.
**Kayıtlar**, bir nesne içindeki tekil girdilerdir — oluşturup yönettiğiniz gerçek verilerdir.
Örneğin:
* "John Smith" is a **record** in the People object
* "Acme Corp" is a **record** in the Companies object
* "John Smith", People nesnesinde bir kayıttır
* "Acme Corp", Companies nesnesinde bir kayıttır
**An analogy:**
**Bir benzetme:**
| Data Model Concept | Real-World Analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **Alan** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
| Veri Modeli Kavramı | Gerçek Dünya Benzetmesi |
| ------------------- | --------------------------------------------------- |
| **Nesneler** | Bir kitaptaki bölümler (kategoriler) |
| **Alan** | Bir elektronik tablodaki sütunlar (özellikler) |
| **Kayıtlar** | Bir elektronik tablodaki satırlar (gerçek girdiler) |
You design the data model (objects + fields) once, then create many records within that structure.
Veri modelini (nesneler + alanlar) bir kez tasarlar, ardından bu yapı içinde birçok kayıt oluşturursunuz.
## Why Customize Your Data Model?
## Veri modelinizi neden özelleştirmelisiniz?
Her iş farklı çalışır. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
Her iş farklı çalışır. Veri modelinizi özelleştirmek, süreçlerinizi katı bir sisteme uydurmaya çalışmak yerine Twenty'yi **sizin** süreçleriniz etrafında şekillendirebilmeniz demektir.
Twenty offers full flexibility:
Twenty tam esneklik sunar:
* Create as many custom objects as you need
* Add unlimited custom fields
* The price doesn't change based on customization
* Gereksinim duyduğunuz kadar özel nesne oluşturun
* Sınırsız özel alan ekleyin
* Fiyat, özelleştirmeye göre değişmez
## Tips to Design Your Data Model
## Veri Modelinizi Tasarlamak İçin İpuçları
### 1. Start with Your Core Objects
### 1. Önce Temel Nesnelerinizle Başlayın
Identify the main concepts you work with. Twenty already provides:
Üzerinde çalıştığınız temel kavramları belirleyin. Twenty zaten şunları sunar:
* **People** — your contacts
* **Companies** — your accounts
* **Opportunities** — your deals
* **People** — kişileriniz
* **Companies** — hesaplarınız
* **Opportunities** — anlaşmalarınız
Think about what else you might need:
Başka nelere ihtiyaç duyabileceğinizi düşünün:
* Stripe would need a `Subscriptions` object
* Airbnb would need a `Trips` object
* An accelerator would need a `Batches` object
* Stripe'ın bir `Subscriptions` nesnesine ihtiyacı olurdu
* Airbnb'nin bir `Trips` nesnesine ihtiyacı olurdu
* Bir hızlandırıcının bir `Batches` nesnesine ihtiyacı olurdu
### 2. Use Fields for Variations, Not New Objects
### 2. Varyasyonlar için yeni nesneler değil, alanlar kullanın
If something is just a characteristic of an existing object, make it a **field**.
Bir şey mevcut bir nesnenin yalnızca bir özelliğiyse, onu bir **alan** yapın.
**Use fields for:**
**Alanları şunlar için kullanın:**
* Categories and labels (e.g., `Industry` for Companies)
* Status values (e.g., `Stage` for Opportunities)
* Attributes and properties
* Kategoriler ve etiketler (ör. Companies için `Industry`)
* Durum değerleri (ör. Opportunities için `Stage`)
* Öznitelikler ve özellikler
### 3. Create an Object When It Stands on Its Own
### 3. Bağımsız Olduğunda Bir Nesne Oluşturun
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
Kavramın kendi yaşam döngüsü, özellikleri veya ilişkileri varsa, bir nesneyi hak eder.
**Create an object for:**
**Şunlar için bir nesne oluşturun:**
* **Projects** — have deadlines, owners, and tasks
* **Subscriptions** — connect companies, products, and invoices
* **Events** — involve attendees and follow-up actions
* **Projects** — son tarihleri, sahipleri ve görevleri olan
* **Subscriptions** — şirketleri, ürünleri ve faturaları bağlar
* **Events** — katılımcıları ve takip eylemlerini içerir
Bunlar, kendi verilerini ve ilişkilerini taşıdığından dolayı tek bir alandan daha fazlasıdır.
### 4. Create an Object When Records Are Open-Ended
### 4. Kayıtlar Ucu Açık Olduğunda Bir Nesne Oluşturun
If something can be linked multiple times and you don't know how many, use an object.
Bir şey birden çok kez ilişkilendirilebiliyorsa ve kaç tane olacağını bilmiyorsanız, bir nesne kullanın.
**Bad approach:**
Creating fields like `Product 1`, `Product 2`, `Product 3`...
**Kötü yaklaşım:**
`Product 1`, `Product 2`, `Product 3` gibi alanlar oluşturmak...
**Good approach:**
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
**İyi yaklaşım:**
Bir `Products` nesnesi oluşturun ve kayıtlarla ilişkilendirin. Bu, modelinizi değiştirmeden bir, iki veya yüz ürünü destekler.
### 5. Keep It Simple First
### 5. Önce Basit Tutun
Start with fields. Move to new objects only when you feel the limits:
Alanlarla başlayın. Move to new objects only when you feel the limits:
* Too many fields on one object
* Repeated records that should be separate
@@ -3,7 +3,7 @@ title: Çalışma Alanı Ayarları
description: Çalışma alanı isminizi ve markanızı özelleştirin.
---
Those are accessible under **Settings → General**.
Bunlara **Ayarlar → Genel** bölümünden erişilebilir.
## Çalışma Alanı Resmi
@@ -16,7 +16,7 @@ Those are accessible under **Settings → General**.
* **İsim**: Çalışma alanı görüntü adınızı değiştirin
* Bu isim tüm çalışma alanı üyelerine görünür
## Danger Zone
## Tehlike bölgesi
<Warning>
Çalışma alanınızı silmek tüm verileri kalıcı olarak kaldırır ve geri alınamaz. Tüm çalışma alanı verileri sonsuza kadar kaybolacak, tüm üyeler anında erişimi kaybedecek ve bu işlem geri döndürülemez.
@@ -1,52 +1,52 @@
---
title: Fields & Columns
description: Choose which fields to display and how to organize them.
title: Alanlar ve Sütunlar
description: Hangi alanların görüntüleneceğini ve bunları nasıl düzenleyeceğinizi seçin.
---
## Selecting Fields to Display
## Görüntülenecek Alanları Seçme
Each view can show a different set of fields. Customize what's visible to focus on the information that matters.
Her görünüm farklı bir alan kümesi gösterebilir. Görünenleri özelleştirerek önemli bilgilere odaklanın.
### Show or Hide Fields
### Alanları Göster veya Gizle
1. Click **Options** in the top right
2. Click **Fields**
3. Click the **eye icon** next to each field to show/hide it
1. Sağ üst köşedeki **Seçenekler**'e tıklayın
2. **Alanlar**'a tıklayın
3. Her alanın yanındaki **göz simgesine** tıklayarak gösterin/gizleyin
### Reorder Fields
### Alanları Yeniden Sıralama
Change the order fields appear in your view:
Görünümünüzde alanların göründüğü sırayı değiştirin:
1. Click **Options → Fields**
2. Drag fields up or down
3. Changes save automatically
1. **Seçenekler → Alanlar**'a tıklayın
2. Alanları yukarı veya aşağı sürükleyin
3. Değişiklikler otomatik olarak kaydedilir
## Field Display by View Type
## Görünüm Türüne Göre Alanların Görüntülenmesi
### Tablo Görünümleri
* Fields appear as columns
* Resize columns by dragging borders
* Alanlar sütun olarak görünür
* Kenarlıkları sürükleyerek sütunları yeniden boyutlandırın
### Kanban Görünümleri
* Fields appear on cards
* Reorder via Options → Fields
* Use Compact view to hide all fields
* Alanlar kartlarda görünür
* Seçenekler → Alanlar üzerinden yeniden sıralayın
* Tüm alanları gizlemek için Kompakt görünümü kullanın
### Calendar Views
### Takvim Görünümleri
* Selected fields show on calendar events
* Configure via Options → Fields
* Seçilen alanlar takvim etkinliklerinde gösterilir
* Seçenekler → Alanlar üzerinden yapılandırın
## En İyi Uygulamalar
* **Show only what's needed** — too many fields clutters the view
* **Put important fields first** — most-used columns on the left
* **Create multiple views** — different field sets for different purposes
* **Use field visibility per view** — same object, different focus
* **Yalnızca gerekenleri gösterin** — çok fazla alan görünümü kalabalıklaştırır
* **Önemli alanları en başa yerleştirin** — en sık kullanılan sütunlar solda
* **Birden çok görünüm oluşturun** — farklı amaçlar için farklı alan kümeleri
* **Görünüme göre alan görünürlüğünü kullanın** — aynı nesne, farklı odak
## Related
## İlgili
* [Table Views](/l/tr/user-guide/views-pipelines/capabilities/table-views) — list view features
* [Kanban Views](/l/tr/user-guide/views-pipelines/capabilities/kanban-views) — card-based views
* [Tablo Görünümleri](/l/tr/user-guide/views-pipelines/capabilities/table-views) — liste görünümü özellikleri
* [Kanban Görünümleri](/l/tr/user-guide/views-pipelines/capabilities/kanban-views) — kart tabanlı görünümler
@@ -1,140 +1,140 @@
---
title: Closed Won Automations
description: Automate post-win activities when opportunities close.
title: Closed Won Otomasyonları
description: Fırsatlar kazanılarak kapandığında kazanım sonrası faaliyetleri otomatikleştirin.
---
When a deal closes, multiple things need to happen: update company status, notify team members, create onboarding tasks. Automate all of this with a single workflow.
Bir anlaşma kapandığında birden çok şeyin olması gerekir: şirket durumunu güncelleyin, ekip üyelerini bilgilendirin, müşteri işe alıştırma görevleri oluşturun. Bunların tümünü tek bir iş akışıyla otomatikleştirin.
## The Problem
## Sorun
When an opportunity moves to "Closed Won":
Bir fırsat "Closed Won" aşamasına geçtiğinde:
* Company type needs to change from "Prospect" to "Customer"
* Onboarding tasks need to be created
* Customer success team needs to be notified
* Sales rep needs confirmation
* Şirket türünün "Prospect"'ten "Customer"'a değişmesi gerekir
* Onboarding görevleri oluşturulmalıdır
* Müşteri başarı ekibinin bilgilendirilmesi gerekir
* Satış temsilcisinin onay alması gerekir
Doing this manually is time-consuming and error-prone.
Bunu elle yapmak zaman alıcıdır ve hataya açıktır.
## The Solution
## Çözüm
Create a workflow that handles all post-win activities automatically.
Kazanım sonrası tüm faaliyetleri otomatik olarak yöneten bir iş akışı oluşturun.
## Complete Workflow Setup
## Tam İş Akışı Kurulumu
### Step 1: Create the Workflow
### Adım 1: İş akışını oluşturun
1. Go to **Settings → Workflows**
2. Click **+ New Workflow**
3. Name it "Deal Won - Post-Win Automation"
1. **Ayarlar → İş Akışları** bölümüne gidin
2. **+ Yeni İş Akışı**'na tıklayın
3. Adını "Deal Won - Post-Win Automation" olarak verin
### Step 2: Configure the Trigger
### Adım 2: Tetikleyiciyi yapılandırın
1. Select **Record is Updated**
2. Choose **Opportunities**
3. Under "Fields to monitor", select **Stage**
1. **Kayıt Güncellendi**'yi seçin
2. **Fırsatlar**'ı seçin
3. "Fields to monitor" altında, **Stage**'i seçin
### Step 3: Add Stage Filter
### Adım 3: Aşama Filtresi ekleyin
1. Add **Filter** action
2. Condition: `{{trigger.object.stage}}` equals "Closed Won"
1. **Filtre** eylemi ekleyin
2. Koşul: `{{trigger.object.stage}}` "Closed Won" değerine eşittir
### Step 4: Update Company Type
### Adım 4: Şirket Türünü Güncelleyin
1. Add **Update Record** action
1. **Kayıt Güncelle** eylemini ekleyin
2. Alanı Yapılandır:
| Alan | Değer |
| ------------------- | ------------------------------- |
| **Object** | Şirketler |
| **Record** | `{{trigger.object.company.id}}` |
| **Tür** | Müşteri |
| **First Deal Date** | `{{trigger.object.closedAt}}` |
| **Hesap Sahibi** | `{{trigger.object.owner.id}}` |
| Alan | Değer |
| ---------------------- | ------------------------------- |
| **Nesne** | Şirketler |
| **Kayıt** | `{{trigger.object.company.id}}` |
| **Tür** | Müşteri |
| **İlk Anlaşma Tarihi** | `{{trigger.object.closedAt}}` |
| **Hesap Sahibi** | `{{trigger.object.owner.id}}` |
### Step 5: Create Onboarding Task
### Adım 5: Onboarding Görevi Oluşturun
1. Add **Create Record** action
1. **Kayıt Oluştur** eylemini ekleyin
2. Alanı Yapılandır:
| Alan | Değer |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Object** | Görevler |
| **Title** | `Onboarding: {{trigger.object.name}}` |
| **Assignee** | Customer Success team member |
| **Due Date** | 3 days from now |
| **Priority** | High |
| **Related Company** | `{{trigger.object.company.id}}` |
| **Related Opportunity** | `{{trigger.object.id}}` |
| **Description** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
| Alan | Değer |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| **Nesne** | Görevler |
| **Başlık** | `Onboarding: {{trigger.object.name}}` |
| **Atanan kişi** | Müşteri başarı ekibi üyesi |
| **Bitiş Tarihi** | Şu andan itibaren 3 gün |
| **Öncelik** | Yüksek |
| **İlgili Şirket** | `{{trigger.object.company.id}}` |
| **İlgili Fırsat** | `{{trigger.object.id}}` |
| **Açıklama** | `{{trigger.object.company.name}} için yeni müşteri uyumlandırması. Anlaşma değeri: {{trigger.object.amount}}` |
### Step 6: Notify Customer Success
### Adım 6: Müşteri Başarı Ekibini Bilgilendirin
1. Add **Send Email** action
1. **E-posta Gönder** eylemini ekleyin
2. Alanı Yapılandır:
| Alan | Değer |
| ----------- | -------------------------------------------------- |
| **To** | customer-success@yourcompany.com |
| **Subject** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Body** | See example below |
| Alan | Değer |
| --------- | -------------------------------------------------- |
| **Kime** | customer-success@yourcompany.com |
| **Konu** | `🎉 Yeni Müşteri: {{trigger.object.company.name}}` |
| **Gövde** | Aşağıdaki örneğe bakın |
**Email body example**:
**E-posta gövdesi örneği**:
```
Hi CS Team,
Merhaba CS Ekibi,
We have a new customer!
Yeni bir müşterimiz var!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
Şirket: {{trigger.object.company.name}}
Anlaşma: {{trigger.object.name}}
Değer: {{trigger.object.amount}}
Satış Temsilcisi: {{trigger.object.owner.name}}
Kapanış Tarihi: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Bir onboarding görevi otomatik olarak oluşturuldu.
Let's give them a great start!
Onlara harika bir başlangıç yapalım!
```
### Step 7: Confirm to Sales Rep
### Adım 7: Satış temsilcisine onay gönderin
1. Add another **Send Email** action
1. Bir tane daha **E-posta Gönder** eylemi ekleyin
2. Alanı Yapılandır:
| Alan | Değer |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| **To** | `{{trigger.object.owner.email}}` |
| **Subject** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Body** | Congratulations! Your deal has been processed. The customer success team has been notified and onboarding has begun. |
| Alan | Değer |
| --------- | --------------------------------------------------------------------------------------------------- |
| **Kime** | `{{trigger.object.owner.email}}` |
| **Konu** | `✅ Anlaşma Kapandı: {{trigger.object.name}}` |
| **Gövde** | Tebrikler! Anlaşmanız işleme alındı. Müşteri başarı ekibi bilgilendirildi ve onboarding başlatıldı. |
### Step 8: Test and Activate
### Adım 8: Test Edin ve Etkinleştirin
1. Test by moving a test opportunity to "Closed Won"
1. Bir test fırsatını "Closed Won" aşamasına taşıyarak test edin
2. Doğrula:
* Company type changed to "Customer"
* Onboarding task created
* CS team received email
* Sales rep received confirmation
3. Activate when ready
* Şirket türü "Customer" olarak değiştirildi
* Onboarding görevi oluşturuldu
* CS ekibi e-postayı aldı
* Satış temsilcisi onayı aldı
3. Hazır olduğunuzda etkinleştirin
## Handling Closed Lost
## Closed Lost'u Yönetme
Create a similar workflow for lost deals:
Kaybedilen anlaşmalar için benzer bir iş akışı oluşturun:
### Tetikleyici
* Record is Updated (Opportunities, Stage = "Closed Lost")
* Kayıt Güncellendi (Fırsatlar, Aşama = "Closed Lost")
### Eylemler
1. **Create Record**: Task for "Lost Deal Analysis"
2. **Update Record**: Add lost reason to company record
3. **Send Email**: Notify manager of lost deal
1. **Kayıt Oluştur**: "Lost Deal Analysis" için görev
2. **Kayıt Güncelle**: Şirket kaydına kaybetme nedenini ekleyin
3. **E-posta Gönder**: Kaybedilen anlaşmayı yöneticiye bildirin
## Advanced: Multi-Step Onboarding
## Gelişmiş: Çok Aşamalı Onboarding
For complex onboarding, create multiple tasks:
Karmaşık onboarding için birden fazla görev oluşturun:
```javascript
export const main = async (params) => {
@@ -149,31 +149,31 @@ export const main = async (params) => {
};
```
Use **Iterator** to create each task from the array.
Diziden her bir görevi oluşturmak için **Iterator** kullanın.
## Customization Ideas
## Özelleştirme Fikirleri
### Keep your other tools up-to-date
### Diğer araçlarınızı güncel tutun
* Create customer in billing system with an **HTTP Request**
* Faturalama sisteminde bir **HTTP Request** ile müşteri oluşturun
### Conditional Actions
### Koşullu Eylemler
Use **Filter** actions to:
**Filtre** eylemlerini şunlar için kullanın:
* Different onboarding for enterprise vs SMB
* Different assignees based on region
* Skip notifications for small deals
* Kurumsal ve KOBİ için farklı onboarding
* Bölgeye göre farklı atanan kişiler
* Küçük anlaşmalar için bildirimleri atlayın
### Include Deal Details
### Anlaşma Ayrıntılarını Dahil Edin
Use **Code** action to format:
Biçimlendirmek için **Kod** eylemini kullanın:
* Deal summary documents
* Handoff notes for CS team
* Custom onboarding checklists
* Anlaşma özet belgeleri
* CS ekibi için devir notları
* Özel onboarding kontrol listeleri
## Related
## İlgili
* [Workflow Actions](/l/tr/user-guide/workflows/capabilities/workflow-actions)
* [Send Emails from Workflows](/l/tr/user-guide/workflows/capabilities/send-emails-from-workflows)
* [İş Akışı Eylemleri](/l/tr/user-guide/workflows/capabilities/workflow-actions)
* [İş Akışlarından E-posta Gönderme](/l/tr/user-guide/workflows/capabilities/send-emails-from-workflows)