Compare commits

..
Author SHA1 Message Date
Sonarly Claude Code dfc0a4d5fc fix: make Application OneToMany relation fields nullable in GraphQL schema
https://sonarly.com/issue/29560?type=bug

The `findManyApplications` GraphQL query fails when a client requests `applicationVariables` (or any of 4 other OneToMany relation fields), because the backend no longer loads those relations but the GraphQL schema still declares them as non-nullable.

Fix: The root cause is a schema-reality mismatch introduced by commit 9a95cd02ed (PR #19892, merged 2026-04-20). That PR intentionally removed OneToMany relation loading from `findManyApplications` to fix Cartesian product timeout issues, but neglected to update the GraphQL schema. Five `@Field()` decorators on ApplicationDTO declared OneToMany relation fields (agents, frontComponents, logicFunctions, objects, applicationVariables) as non-nullable, but multiple code paths return ApplicationDTO instances without those fields populated.

      The fix adds `{ nullable: true }` to all five `@Field()` decorators, making the GraphQL schema accurately reflect that these fields are optionally loaded. This is consistent with the TypeScript type annotations (all five properties already had `?` optional markers) and with the existing pattern used by other optional fields in the same DTO (e.g., defaultLogicFunctionRole, applicationRegistration).

      This resolves the Sentry error "Cannot return null for non-nullable field Application.applicationVariables" and prevents the same class of error for the other four relation fields. The fix is safe because: (1) the standard frontend FIND_MANY_APPLICATIONS query doesn't request these fields, (2) the frontend FIND_ONE_APPLICATION query does request them but that code path (findOneApplication) properly loads all relations via Promise.all, and (3) any third-party API consumers or SDK users will now get null instead of a hard GraphQL error when these fields aren't loaded.
2026-04-21 19:17:47 +00:00
92c8965dd3 i18n - docs translations (#19944)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-04-21 20:43:22 +02:00
Abdul RahmanandGitHub f19b3bfd38 Fix left/right arrow keys not working in dropdown search inputs (#19759)
Closes #12847 

Implements a two-mode focus management pattern for `SelectableList`
components with search inputs, resolving the conflict between text input
cursor movement and grid/list navigation.

### How it works

**Input mode** (search input focused):
- Left/right arrow keys move the text cursor normally
(`enableOnFormTags: false` on ArrowLeft/ArrowRight hotkeys)
- Up/down arrow keys blur the input and transfer focus to the grid,
entering grid mode

**Grid mode** (search input blurred):
- All arrow keys navigate the selectable list grid
- Pressing up arrow from the top row clears the grid selection and
refocuses the search input, returning to input mode
- Typing any printable character refocuses the search input (wildcard
hotkey with `enableOnFormTags: false`)

### Demo 


https://github.com/user-attachments/assets/825ad603-a5f8-4863-8269-3ecf35965847


https://github.com/user-attachments/assets/9d07346d-18a0-40fa-8874-21040c11f03d
2026-04-21 18:25:44 +00:00
0aaffe3212 i18n - docs translations (#19943)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-04-21 18:44:52 +02:00
Paul RastoinandGitHub 66857ca77b Remove cross version upgrade placeholder (#19940)
Moving to somewhere else to leverage docker cache
2026-04-21 16:16:47 +00:00
WeikoandGitHub 76582dc04a Export generateDefaultFieldUniversalIdentifier from SDK (#19937)
## Context
- Moved generateDefaultFieldUniversalIdentifier from the internal
cli/utilities/build/manifest/utils/ folder to sdk/define/objects/ and
re-exported it from
the public twenty-sdk/define entry point, so app authors can compute the
deterministic UID
of a default field (id, name, createdAt, …) from their own code.
- Narrowed the signature from { objectConfig: ObjectConfig; fieldName }
to {
objectUniversalIdentifier: string; fieldName: string } — the only thing
the helper needs,
and what app code has on hand.
- Updated the two internal callers and the spec to the new import path
and signature.

```typescript
import {
  defineView,
  generateDefaultFieldUniversalIdentifier,
} from "twenty-sdk/define";

export default defineView({
  universalIdentifier: "70f10d44-144a-4da8-8c6f-3ec2422138c0",
  name: "all-thing",
  objectUniversalIdentifier: "c782b61c-70fd-4c88-9cd6-4e61ab8d7591",
  icon: "IconList",
  position: 0,
  fields: [
    {
      universalIdentifier: "75a90bc4-d901-4df4-85e0-af29db5e0104",
      fieldMetadataUniversalIdentifier: generateDefaultFieldUniversalIdentifier(
        {
          objectUniversalIdentifier: "c782b61c-70fd-4c88-9cd6-4e61ab8d7591",
          fieldName: "createdAt",
        },
      ),
      position: 0,
      isVisible: true,
      size: 200,
    },
    {
      universalIdentifier: "75a90bc5-d901-4df4-85e0-af29db5e0105",
      fieldMetadataUniversalIdentifier: generateDefaultFieldUniversalIdentifier(
        {
          objectUniversalIdentifier: "c782b61c-70fd-4c88-9cd6-4e61ab8d7591",
          fieldName: "createdBy",
        },
      ),
      position: 0,
      isVisible: true,
      size: 200,
    },
  ],
});
```
2026-04-21 16:07:09 +00:00
neo773andGitHub a2099d22b5 Optimize website images (#19933)
AVIF has broad support in 2026 we should leverage it for smaller image
size


<img width="1496" height="847" alt="Screenshot 2026-04-21 at 5 49 58 PM"
src="https://github.com/user-attachments/assets/8366a2c7-f72b-4cff-891e-78a372d9a84b"
/>

```
     /halftone/materials/glass/environment.jpg                                    41KB       31KB      23%
     /illustrations/generated/handshake.png                                       61KB       49KB      19%
     /illustrations/generated/home-background-bridge.png                          19KB       13KB      27%
     /illustrations/generated/mic.png                                             47KB       32KB      32%
     /illustrations/generated/milestone.jpg                                      101KB       89KB      12%
     /illustrations/generated/partner-meeting.webp                                78KB       37KB      52%
     /images/case-studies/header-pattern.png                                     250KB      165KB      33%
     /images/home/hero/background.webp                                           101KB       73KB      26%
     /images/home/hero/foreground.webp                                            60KB       37KB      38%
     /images/home/hero/sales-dashboard/distribution.webp                          11KB        7KB      29%
     /images/home/hero/sales-dashboard/revenue.webp                               15KB        9KB      37%
     /images/home/hero/sales-dashboard/visits.webp                                14KB       11KB      21%
     /images/home/hero/twenty-demo-logo.webp                                       0KB        0KB     -99%
     /images/home/logo-bar/bayer.webp                                             52KB       37KB      29%
     /images/home/logo-bar/french-republic.webp                                    6KB        6KB       5%
     /images/home/logo-bar/nic.webp                                                4KB        4KB      -3%
     /images/home/logo-bar/pwc.webp                                                3KB        2KB      10%
     /images/home/logo-bar/shiawase-home.webp                                      7KB        8KB      -2%
     /images/home/logo-bar/windmill-logo.png                                       2KB        3KB     -17%
     /images/home/logo-bar/windmill-original.webp                                  2KB        2KB       8%
     /images/home/problem/monolith-problem.webp                                   23KB       19KB      13%
     /images/home/stepper/background-shape.webp                                    1KB        0KB      34%
     /images/home/stepper/background.webp                                         78KB       36KB      53%
     /images/home/stepper/download-worker.webp                                    21KB       19KB       6%
     /images/home/stepper/gears.jpg                                               24KB       22KB       6%
     /images/home/three-cards-feature/familiar-interface-gradient.webp             7KB        8KB     -11%
     /images/home/three-cards-feature/familiar-interface.webp                     62KB       45KB      27%
     /images/home/three-cards-feature/fast-path-background-noise.webp             49KB       16KB      66%
     /images/home/three-cards-feature/fast-path-gradient.webp                      7KB        8KB      -7%
     /images/home/three-cards-feature/fast-path.webp                              52KB       41KB      21%
     /images/home/three-cards-feature/live-data-gradient.webp                      3KB        2KB      35%
     /images/home/three-cards-feature/live-data.webp                              46KB       36KB      20%
     /images/partner/hero/hero.webp                                              123KB       97KB      20%
     /images/partner/hero/partners-hero.webp                                      42KB       32KB      23%
     /images/partner/testimonials/amrendra-singh.webp                              6KB        5KB       7%
     /images/partner/testimonials/benjamin-reynolds.webp                         111KB       80KB      27%
     /images/partner/testimonials/bertrams.jpeg                                   12KB       10KB       9%
     /images/partner/testimonials/joseph-chiang.jpg                               13KB       11KB      13%
     /images/partner/testimonials/mike-babiy.png                                  24KB       19KB      20%
     /images/partner/testimonials/olivier-reinaud.jpg                             11KB        9KB      20%
     /images/pricing/engagement-band/overlay.webp                                 72KB       56KB      22%
     /images/pricing/plans/organization-icon.png                                   4KB        3KB      10%
     /images/pricing/plans/pro-icon.png                                            2KB        3KB      -7%
     /images/pricing/salesforce/help-icon.webp                                     0KB        0KB     -45%
     /images/product/demo/background.webp                                         84KB       70KB      16%
     /images/product/demo/kanban.webp                                             63KB       57KB       9%
     /images/product/feature/contacts.webp                                        44KB       32KB      28%
     /images/product/feature/dashboards.webp                                      24KB       21KB      14%
     /images/product/feature/data.webp                                            23KB       20KB      13%
     /images/product/feature/emails.webp                                          21KB       18KB      12%
     /images/product/feature/files.webp                                           14KB       12KB      14%
     /images/product/feature/mask.webp                                           148KB       82KB      44%
     /images/product/feature/pipeline.webp                                        30KB       26KB      11%
     /images/product/feature/tasks.webp                                           37KB       33KB       9%
     /images/product/stepper/background-shape.webp                                 1KB        1KB      25%
     /images/product/stepper/background.webp                                      61KB       52KB      13%
     /images/product/stepper/step-one.webp                                         7KB        7KB       0%
     /images/product/stepper/step-three.webp                                      17KB       14KB      13%
     /images/product/stepper/step-two.webp                                         7KB        7KB       3%
     /images/product/tabs/background-shape.webp                                    7KB        1KB      83%
     /images/product/tabs/background.webp                                        180KB      171KB       5%
     /images/product/tabs/deals.webp                                              93KB       89KB       4%
     /images/product/tabs/history.webp                                            66KB       59KB      10%
     /images/product/tabs/tasks.webp                                              90KB       85KB       5%
     /images/product/tabs/workflow.webp                                           60KB       55KB       7%
     /images/releases/0.10/0.10-currency.webp                                     40KB       28KB      29%
     /images/releases/0.10/0.10-datetime.webp                                     34KB       18KB      45%
     /images/releases/0.10/0.10-json.webp                                         35KB       25KB      26%
     /images/releases/0.10/0.10-multi-select.webp                                 46KB       36KB      22%
     /images/releases/0.10/0.10-remote.webp                                       45KB       27KB      39%
     /images/releases/0.11/0.11-calendar.webp                                     69KB       56KB      18%
     /images/releases/0.11/0.11-speed.webp                                        30KB       21KB      29%
     /images/releases/0.12/0.12-database-diagram.webp                             35KB       25KB      29%
     /images/releases/0.12/0.12-link-field.webp                                   50KB       35KB      30%
     /images/releases/0.12/0.12-loader.webp                                       57KB       35KB      38%
     /images/releases/0.12/0.12-notifications.webp                                78KB       64KB      17%
     /images/releases/0.2.3_relations.webp                                        55KB       39KB      28%
     /images/releases/0.2.3_webhooks.webp                                         30KB       25KB      16%
     /images/releases/0.20/0.20-blocklist.webp                                    35KB       27KB      22%
     /images/releases/0.20/0.20-onboarding.webp                                   68KB       53KB      21%
     /images/releases/0.20/0.20-timeline.webp                                     81KB       55KB      32%
     /images/releases/0.21/0.21-advanced-email-settings.webp                      42KB       30KB      27%
     /images/releases/0.21/0.21-many-many.webp                                    60KB       46KB      22%
     /images/releases/0.22/0.22-kanban-improvements.webp                          50KB       34KB      31%
     /images/releases/0.22/0.22-mass-deletion.webp                                32KB       20KB      37%
     /images/releases/0.22/0.22-navbar.webp                                       35KB       23KB      34%
     /images/releases/0.23/0.23-created-by.webp                                   78KB       60KB      23%
     /images/releases/0.23/0.23-filter-webhooks.webp                              54KB       39KB      28%
     /images/releases/0.23/0.23-notes-tasks.webp                                  83KB       62KB      25%
     /images/releases/0.24/0.24-soft-delete.webp                                  70KB       47KB      31%
     /images/releases/0.3.0_rating.webp                                           50KB       46KB       8%
     /images/releases/0.3.1_contributors.webp                                     40KB       32KB      20%
     /images/releases/0.3.2_new_layout.webp                                       52KB       37KB      28%
     /images/releases/0.3.3_emails.webp                                           48KB       38KB      21%
     /images/releases/0.3.3_kanban.webp                                           44KB       35KB      19%
     /images/releases/0.3.3_sign_up.webp                                          24KB       17KB      25%
     /images/releases/0.30/0.30-array-field.webp                                  90KB       68KB      24%
     /images/releases/0.30/0.30-emails.webp                                       44KB       30KB      31%
     /images/releases/0.30/0.30-new-settings.webp                                 31KB       19KB      37%
     /images/releases/0.31/0.31-advanced-settings.webp                            44KB       30KB      31%
     /images/releases/0.31/0.31-search.webp                                       18KB       12KB      31%
     /images/releases/0.32/0.32-improved-cmdk.webp                                50KB       33KB      33%
     /images/releases/0.32/0.32-webhooks.webp                                     41KB       30KB      26%
     /images/releases/0.33/0.33-multiselect-filter.webp                           53KB       36KB      31%
     /images/releases/0.33/0.33-percentage-number.webp                            33KB       20KB      38%
     /images/releases/0.34/0.34-subdomains.webp                                   57KB       41KB      28%
     /images/releases/0.35/0.35-Favorites.webp                                    61KB       41KB      32%
     /images/releases/0.4/0.4-address-field-type.webp                             23KB       15KB      32%
     /images/releases/0.4/0.4-expand-relation-card.webp                           28KB       18KB      34%
     /images/releases/0.4/0.4-multi-workspace.webp                                48KB       35KB      25%
     /images/releases/0.40/0.40-aggregates.webp                                   72KB       57KB      21%
     /images/releases/0.40/0.40-group-by.webp                                     47KB       32KB      31%
     /images/releases/0.41/0.41-labs.webp                                         55KB       40KB      27%
     /images/releases/0.42/0.42-document-viewer.webp                              86KB       72KB      15%
     /images/releases/0.42/0.42-microsoft.webp                                    79KB       64KB      18%
     /images/releases/0.42/0.42-translation.webp                                  66KB       53KB      19%
     /images/releases/0.43.0/email-privacy.webp                                   47KB       33KB      27%
     /images/releases/0.43.0/search-upgrade.webp                                  44KB       30KB      31%
     /images/releases/0.44/0.44-admin-panel.webp                                  57KB       42KB      25%
     /images/releases/0.44/0.44-side-panel.webp                                   67KB       54KB      20%
     /images/releases/0.50/0.50-advanced-filters.webp                             55KB       36KB      33%
     /images/releases/0.50/0.50-permissions.webp                                  51KB       37KB      25%
     /images/releases/0.51.0/0.51-options-menu.webp                               59KB       40KB      31%
     /images/releases/0.52.0/0.52-custom-date-format.webp                         37KB       23KB      36%
     /images/releases/0.52.0/0.52-filtered-views-records.webp                     46KB       31KB      31%
     /images/releases/1.00/1.00-import-update.webp                                63KB       39KB      37%
     /images/releases/1.00/1.00-performance-improvement.webp                      52KB       35KB      31%
     /images/releases/1.00/1.00-permissions.webp                                  72KB       52KB      28%
     /images/releases/1.00/1.00-subfield-filtering.webp                           73KB       49KB      33%
     /images/releases/1.00/1.00-workflow.webp                                     52KB       36KB      29%
     /images/releases/1.1/1.1-multi-manual-trigger.webp                           51KB       37KB      26%
     /images/releases/1.10/1.10.0-calendar.webp                                   54KB       38KB      28%
     /images/releases/1.10/1.10.0-dashboards.webp                                 33KB       20KB      37%
     /images/releases/1.11/1.11.0-morph-relations.webp                            53KB       40KB      24%
     /images/releases/1.11/1.11.0-unlisted-views.webp                             50KB       34KB      32%
     /images/releases/1.12/1.12.0-folder-sync.webp                                38KB       25KB      32%
     /images/releases/1.12/1.12.0-side-panel.webp                                 45KB       34KB      22%
     /images/releases/1.13/1.13.0-stop-workflow-button.png                        39KB       26KB      34%
     /images/releases/1.14/1.14.0-resize-navbar-and-side-panel.png                58KB       40KB      30%
     /images/releases/1.15/1.15.0-updated-by-official.webp                        36KB       23KB      35%
     /images/releases/1.15/1.15.0-updated-by.png                                  36KB       27KB      24%
     /images/releases/1.15/1.15.0-updated-by.webp                                 36KB       23KB      35%
     /images/releases/1.16/1.16.0-files-in-records.webp                           43KB       29KB      32%
     /images/releases/1.16/1.16.0-flexible-relations.webp                         42KB       30KB      28%
     /images/releases/1.17/1.17.0-ai-chat.webp                                    83KB       67KB      18%
     /images/releases/1.18/1.18.0-live-updates.webp                               56KB       37KB      33%
     /images/releases/1.18/1.18.0-sidebar-items.webp                              36KB       27KB      26%
     /images/releases/1.19/1.19.0-invite-roles.webp                               40KB       26KB      34%
     /images/releases/1.2/1.2-any-fields.webp                                     47KB       30KB      35%
     /images/releases/1.2/1.2-import-relations.webp                               42KB       31KB      26%
     /images/releases/1.20/1.20.0-easier-field-editing.webp                       37KB       26KB      27%
     /images/releases/1.20/1.20.0-field-widgets.webp                              38KB       29KB      24%
     /images/releases/1.21/1.21.0-email-replies.webp                              50KB       38KB      25%
     /images/releases/1.21/1.21.0-maintenance-mode.webp                           55KB       39KB      28%
     /images/releases/1.22/1.22.0-rich-text-layouts.webp                          54KB       41KB      24%
     /images/releases/1.23/1.23.0-easier-layouts.webp                             46KB       30KB      34%
     /images/releases/1.3/1.3-IMAP.webp                                           83KB       59KB      29%
     /images/releases/1.3/1.3-merge.webp                                          64KB       44KB      30%
     /images/releases/1.4/1.4-field-permissions.webp                              50KB       37KB      24%
     /images/releases/1.4/1.4-two-factor-auth.webp                                67KB       50KB      25%
     /images/releases/1.4/1.4-workflow-filters.webp                               38KB       26KB      31%
     /images/releases/1.5/1.5-workflow-branches.webp                              31KB       21KB      31%
     /images/releases/1.6/1.6-workflows-improvements.webp                         45KB       31KB      29%
     /images/releases/1.7/1.7-impersonating.webp                                  79KB       60KB      23%
     /images/releases/1.7/1.7-upsert.webp                                         51KB       31KB      38%
     /images/releases/1.8/1.8-bulk-select.webp                                    49KB       34KB      29%
     /images/releases/1.8/1.8-search-limit.webp                                   34KB       23KB      31%
     /images/releases/1.8/1.8-workflow-iterator.webp                              38KB       23KB      39%
     /images/releases/labs/translation.webp                                       65KB       46KB      28%
     /images/shared/companies/logos/a16z.png                                       0KB        0KB     -48%
     /images/shared/companies/logos/accel.png                                      0KB        0KB    -129%
     /images/shared/companies/logos/airbnb.png                                     0KB        0KB     -52%
     /images/shared/companies/logos/airtable.png                                   0KB        1KB     -42%
     /images/shared/companies/logos/anthropic.png                                  0KB        0KB     -45%
     /images/shared/companies/logos/apple-1977.png                                 0KB        1KB     -54%
     /images/shared/companies/logos/apple.png                                      0KB        0KB     -89%
     /images/shared/companies/logos/calendar.png                                   0KB        0KB     -48%
     /images/shared/companies/logos/claude.png                                    40KB       31KB      21%
     /images/shared/companies/logos/cursor.png                                     0KB        0KB     -58%
     /images/shared/companies/logos/docusign.png                                   0KB        0KB     -48%
     /images/shared/companies/logos/figma.png                                      0KB        0KB     -46%
     /images/shared/companies/logos/founders-fund.png                              0KB        0KB     -61%
     /images/shared/companies/logos/github.png                                     0KB        0KB     -37%
     /images/shared/companies/logos/gmail.png                                      0KB        1KB     -60%
     /images/shared/companies/logos/google.png                                     0KB        1KB     -36%
     /images/shared/companies/logos/hubspot.png                                    0KB        0KB      -2%
     /images/shared/companies/logos/kleiner-perkins.png                            0KB        0KB    -105%
     /images/shared/companies/logos/lemlist.png                                    0KB        0KB     -64%
     /images/shared/companies/logos/linkedin.png                                   0KB        0KB     -66%
     /images/shared/companies/logos/mailchimp.png                                  0KB        0KB     -39%
     /images/shared/companies/logos/meet.png                                       0KB        0KB     -51%
     /images/shared/companies/logos/metabase.png                                   1KB        1KB     -32%
     /images/shared/companies/logos/microsoft.png                                  0KB        0KB       4%
     /images/shared/companies/logos/notion.png                                     0KB        1KB     -19%
     /images/shared/companies/logos/okta.png                                       0KB        0KB     -35%
     /images/shared/companies/logos/openai.png                                     0KB        1KB     -13%
     /images/shared/companies/logos/outlook.png                                    0KB        0KB     -26%
     /images/shared/companies/logos/outreach.png                                   0KB        0KB     -53%
     /images/shared/companies/logos/postgresql.png                                 1KB        1KB      -2%
     /images/shared/companies/logos/qonto.png                                      0KB        0KB     -61%
     /images/shared/companies/logos/salesforce.png                                 0KB        0KB     -39%
     /images/shared/companies/logos/segment.png                                    0KB        1KB     -49%
     /images/shared/companies/logos/sequoia.png                                    0KB        0KB     -59%
     /images/shared/companies/logos/slack.png                                      0KB        0KB     -40%
     /images/shared/companies/logos/stripe.png                                     0KB        0KB     -51%
     /images/shared/companies/logos/tally.png                                      0KB        0KB     -53%
     /images/shared/companies/logos/twenty.png                                     0KB        0KB     -56%
     /images/shared/companies/logos/whatsapp.png                                   0KB        0KB     -64%
     /images/shared/companies/logos/zapier.png                                     0KB        0KB     -27%
     /images/shared/engagement-band/halftone-on-gray.png                           7KB       17KB    -121%
     /images/shared/engagement-band/halftone-on-white.png                          2KB        2KB      -2%
     /images/shared/light-noise.webp                                               0KB        0KB    -225%
     /images/shared/menu/developers-preview.png                                   65KB       33KB      49%
     /images/shared/menu/partners-preview.png                                     52KB       24KB      53%
     /images/shared/menu/user-guide-preview.png                                   58KB       50KB      14%
     /images/shared/people/avatars/alexandre-prot.jpg                              0KB        0KB     -32%
     /images/shared/people/avatars/anonymous-anna.jpg                              0KB        0KB     -81%
     /images/shared/people/avatars/anonymous-fabrice.jpg                           0KB        0KB     -28%
     /images/shared/people/avatars/anonymous-felix.jpg                             0KB        0KB     -40%
     /images/shared/people/avatars/anonymous-indira.jpg                            0KB        0KB     -44%
     /images/shared/people/avatars/anonymous-laura.jpg                             0KB        0KB     -52%
     /images/shared/people/avatars/anonymous-mike.jpg                              0KB        0KB     -78%
     /images/shared/people/avatars/anonymous-thomas.jpg                            0KB        0KB     -44%
     /images/shared/people/avatars/ben-chestnut.jpg                                0KB        0KB     -74%
     /images/shared/people/avatars/brian-chesky.jpg                                0KB        0KB     -52%
     /images/shared/people/avatars/chris-wanstrath.jpg                             0KB        0KB     -46%
     /images/shared/people/avatars/craig-federighi.jpg                             0KB        0KB     -63%
     /images/shared/people/avatars/dario-amodei.jpg                               15KB       14KB       4%
     /images/shared/people/avatars/dylan-field.jpg                                 0KB        0KB     -62%
     /images/shared/people/avatars/eddy-cue.jpg                                    0KB        0KB     -54%
     /images/shared/people/avatars/ivan-zhao.jpg                                   0KB        0KB     -69%
     /images/shared/people/avatars/jeff-williams.jpg                               0KB        0KB     -59%
     /images/shared/people/avatars/joe-gebbia.jpg                                  0KB        0KB     -52%
     /images/shared/people/avatars/katherine-adams.jpg                             0KB        0KB     -64%
     /images/shared/people/avatars/patrick-collison.jpg                            0KB        0KB     -20%
     /images/shared/people/avatars/peter-reinhardt.jpg                             0KB        0KB     -87%
     /images/shared/people/avatars/peter-thiel.jpg                                 0KB        0KB     -57%
     /images/shared/people/avatars/phil-schiller.jpg                               0KB        0KB     -52%
     /images/shared/people/avatars/ping-li.jpg                                    13KB       12KB      10%
     /images/shared/people/avatars/ray-damm.jpg                                    0KB        0KB     -41%
     /images/shared/people/avatars/reid-hoffman.jpg                                0KB        0KB     -60%
     /images/shared/people/avatars/roelof-botha.jpg                                0KB        0KB     -41%
     /images/shared/people/avatars/ryan-roslansky.jpg                              0KB        0KB     -46%
     /images/shared/people/avatars/steve-anavi.jpg                                 0KB        0KB     -32%
     /images/shared/people/avatars/stewart-butterfield.jpg                         0KB        0KB     -36%
     /images/shared/people/avatars/sundar-pichai.jpg                               0KB        0KB     -41%
     /images/shared/people/avatars/thomas-dohmke.jpg                              15KB       14KB       4%
     /images/shared/people/avatars/tim-cook.jpg                                    0KB        0KB     -68%
     /images/why-twenty/hero/background.webp                                      10KB        9KB      11%

     === TOTALS ===
     Files: 249
     Total WebP: 8.2 MB
     Total AVIF: 6.1 MB
     Total saved: 2.1 MB (26%)
```
2026-04-21 18:15:38 +02:00
neo773andGitHub a7b10b281c Optimize 3d models (#19935)
Remove unused textures and UV data from 3D models
Also simplified meshes where possible and re-compressed
Verified that no visual regression was seen but needs to be double
checked in case I missed something

Total model size: 5.1MB -> 1.5MB
2026-04-21 18:13:03 +02:00
Abdul RahmanandGitHub 3eabdf302e Allow closing navbar folder while viewing an active child item (#19936)
### Before


https://github.com/user-attachments/assets/f8c5c075-b086-4a4c-9514-c7cbbf9b7c56




### After


https://github.com/user-attachments/assets/37427428-046c-4815-a57e-bc5c4fcc7f68
2026-04-21 14:49:31 +00:00
8bb98c309a i18n - docs translations (#19939)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-04-21 16:52:23 +02:00
EtienneandGitHub 8f34a02fea Import - Fix (#19938)
Fixes
https://discord.com/channels/1130383047699738754/1496121832619774042/1496121832619774042

Before
<img width="1089" height="734" alt="Screenshot 2026-04-21 at 16 23 27"
src="https://github.com/user-attachments/assets/6fe17bc1-b1ec-4b5d-a922-d472ff4c4f2f"
/>
After
<img width="1089" height="731" alt="Screenshot 2026-04-21 at 16 22 56"
src="https://github.com/user-attachments/assets/ab7e8fe4-8188-4222-a237-992b73a1594e"
/>
2026-04-21 14:39:34 +00:00
Paul RastoinandGitHub 61fdb613e6 Reset default app packages command (#19931)
# Introduction
If a self host creates its twenty instance using storage type local, and
then edit through the admin panel the storage type, the apps default
deps file won't be swapped to the new storage location

This command allow to manually rebuild them

## What could be done in addition
- We could display a modal in the admin panel when the user is editing
env variable that might have a side effect
- We might wanna rebuild the deps by default if we detect such a change
through the UI though we can't really if it's through the `.env` so I'm
not sure we wanna prio such logic
2026-04-21 13:32:59 +00:00
Abdullah.andGitHub 7947b1b843 Fix self-hosting pricing page design. (#19930)
Resolve:
https://discord.com/channels/1130383047699738754/1496108238909739079
2026-04-21 13:03:17 +00:00
Abdullah.andGitHub 2b399fc94e [Website] Fix flickering of faq illustration. (#19920)
Before:


https://github.com/user-attachments/assets/da189675-5de2-47be-b491-0d52eb11331e

After:


https://github.com/user-attachments/assets/7e382523-3539-4978-873c-e4ea79e264a7
2026-04-21 15:05:58 +02:00
44ba7725ae i18n - docs translations (#19934)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-04-21 14:46:37 +02:00
Paul RastoinandGitHub 65e01400c0 Cross version ci placeholder (#19932)
Created this empty workflow so it appears on main and is pickable from a
different branch to start testing the whole flow
2026-04-21 14:19:28 +02:00
130 changed files with 1664 additions and 1277 deletions
@@ -1,6 +1,6 @@
---
title: الأخطاء والطلبات وطلبات السحب
icon: bug
icon: خلل
info: أبلغ عن المشكلات، واطلب الميزات، وساهم بالشفرة البرمجية
---
@@ -1,12 +1,12 @@
---
title: Commands
title: أوامر
icon: terminal
description: Useful commands for developing Twenty.
description: أوامر مفيدة لتطوير Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
يمكن تشغيل الأوامر من جذر المستودع باستخدام `npx nx`. استخدم `npx nx run {project}:{command}` للاستهداف الصريح.
## Starting the App
## بدء التطبيق
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## قاعدة البيانات
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -22,7 +22,7 @@ npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
## فحص الشيفرة
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
@@ -30,14 +30,14 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## التحقق من الأنواع
```bash
npx nx typecheck twenty-front
npx nx typecheck twenty-server
```
## "الاختبار"
## الاختبار
```bash
# Frontend
@@ -61,14 +61,14 @@ npx nx run twenty-front:graphql:generate # Regenerate typ
npx nx run twenty-front:graphql:generate --configuration=metadata # Metadata schema
```
## "الترجمات"
## الترجمات
```bash
npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## البناء
```bash
npx nx build twenty-shared # Must be built first
@@ -1,14 +1,14 @@
---
title: دليل الأسلوب
icon: paintbrush
description: Code conventions and best practices for contributing to Twenty.
icon: فرشاة الرسم
description: اتفاقيات الشيفرة وأفضل الممارسات للمساهمة في Twenty.
---
## React
### Functional components only
### المكوّنات الوظيفية فقط
Always use TSX functional components with named exports.
استخدم دائمًا مكوّنات TSX الوظيفية مع تصديرات مسمّاة.
```tsx
// ❌ Bad
@@ -23,9 +23,9 @@ export function MyComponent() {
};
```
### الإزاحة
### الخصائص
Create a type named `{ComponentName}Props`. Use destructuring. Don't use `React.FC`.
أنشئ نوعًا باسم `{ComponentName}Props`. استخدم التفكيك. لا تستخدم `React.FC`.
```tsx
type MyComponentProps = {
@@ -35,7 +35,7 @@ type MyComponentProps = {
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### No single-variable prop spreading
### لا تستخدم نشر الخصائص من متغير واحد
```tsx
// ❌ Bad
@@ -45,9 +45,9 @@ const MyComponent = (props: MyComponentProps) => <Other {...props} />;
const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1, prop2 }} />;
```
## "إدارة الحالة"
## إدارة الحالة
### Jotai atoms for global state
### ذرات Jotai للحالة العامة
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
@@ -59,16 +59,16 @@ export const myAtomState = createAtomState<string>({
});
```
* Prefer atoms over prop drilling
* Don't use `useRef` for state — use `useState` or atoms
* Use atom families and selectors for lists
* فضّل الذرات على تمرير الخصائص عبر المستويات (prop drilling)
* لا تستخدم `useRef` للحالة — استخدم `useState` أو الذرات
* استخدم عائلات الذرات والمحددات للقوائم
### Avoid unnecessary re-renders
### تجنّب عمليات إعادة التصيير غير الضرورية
* Extract `useEffect` and data fetching into sibling sidecar components
* Prefer event handlers (`handleClick`, `handleChange`) over `useEffect`
* Don't use `React.memo()` — fix the root cause instead
* Limit `useCallback` / `useMemo` usage
* انقل `useEffect` وجلب البيانات إلى مكوّنات جانبية شقيقة (sidecar)
* فضّل معالِجات الأحداث (`handleClick`, `handleChange`) على `useEffect`
* لا تستخدم `React.memo()` — أصلِح السبب الجذري بدلًا من ذلك
* حدّد استخدام `useCallback` / `useMemo`
```tsx
// ❌ Bad — useEffect in the same component causes re-renders
@@ -94,11 +94,11 @@ export const Page = () => {
## TypeScript
* **`type` over `interface`** — more flexible, easier to compose
* **String literals over enums** — except for GraphQL codegen enums and internal library APIs
* **No `any`** — strict TypeScript enforced
* **No type imports** — use regular imports (enforced by Oxlint `typescript/consistent-type-imports`)
* **Use [Zod](https://github.com/colinhacks/zod)** for runtime validation of untyped objects
* **`type` بدلًا من `interface`** — أكثر مرونة وأسهل في التركيب
* **النصوص الحرفية بدل التعدادات** — باستثناء تعدادات codegen الخاصة بـ GraphQL وواجهات برمجة تطبيقات المكتبة الداخلية
* **بدون `any`** — يتم فرض TypeScript الصارم
* **عدم استيراد الأنواع** — استخدم استيرادات عادية (مفروض بواسطة Oxlint `typescript/consistent-type-imports`)
* **استخدم [Zod](https://github.com/colinhacks/zod)** للتحقق وقت التشغيل من الكائنات غير محددة النوع
## JavaScript
@@ -112,17 +112,17 @@ onClick?.();
## التسمية
* **Variables**: camelCase, descriptive (`email` not `value`, `fieldMetadata` not `fm`)
* **Constants**: SCREAMING_SNAKE_CASE
* **Types/Classes**: PascalCase
* **Files/directories**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Event handlers**: `handleClick` (not `onClick` for the handler function)
* **Component props**: prefix with component name (`ButtonProps`)
* **Styled components**: prefix with `Styled` (`StyledTitle`)
* **المتغيرات**: camelCase، وصفية (`email` وليس `value`، `fieldMetadata` وليس `fm`)
* **الثوابت**: SCREAMING_SNAKE_CASE
* **الأنواع/الفئات**: PascalCase
* **الملفات/المجلدات**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **معالجات الأحداث**: `handleClick` (وليس `onClick` لدالة المعالج)
* **خصائص المكوّن**: ابدأ باسم المكوّن (`ButtonProps`)
* **مكوّنات Styled**: ابدأ بـ `Styled` (`StyledTitle`)
## التنسيق
Use [Linaria](https://github.com/callstack/linaria) styled components. Use theme values — avoid hardcoded `px`, `rem`, or colors.
استخدم مكوّنات [Linaria](https://github.com/callstack/linaria) المنسقة. استخدم قيم السمة — وتجنّب القيم المضمّنة صراحة مثل `px` و`rem` أو الألوان.
```tsx
// ❌ Bad
@@ -142,7 +142,7 @@ const StyledButton = styled.button`
## استيرادات
Use aliases instead of relative paths:
استخدم الأسماء المستعارة بدل المسارات النسبية:
```tsx
// ❌ Bad
@@ -153,7 +153,7 @@ import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## Folder Structure
## هيكلية المجلدات
```
front
@@ -171,6 +171,6 @@ front
└── ui/ # Reusable UI components (display, input, feedback, ...)
```
* Modules can import from other modules, but `ui/` should stay dependency-free
* Use `internal/` subfolders for module-private code
* Components under 300 lines, services under 500 lines
* يمكن للوحدات الاستيراد من وحدات أخرى، لكن يجب أن يبقى `ui/` خاليًا من التبعيات
* استخدم المجلدات الفرعية `internal/` للشيفرة الخاصة بالوحدة
* المكوّنات أقل من 300 سطر، والخدمات أقل من 500 سطر
@@ -1,35 +1,35 @@
---
title: واجهات برمجة التطبيقات
icon: plug
description: REST and GraphQL APIs generated from your workspace schema.
description: واجهات برمجة تطبيقات REST وGraphQL مُولَّدة من مخطط مساحة عملك.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## Schema-per-tenant APIs
## واجهات برمجة تطبيقات بمخطط لكل مستأجر
There is no static API reference for Twenty. Each workspace has its own schema — when you add a custom object (say `Invoice`), it immediately gets REST and GraphQL endpoints identical to built-in objects like `Company` or `Person`. The API is generated from the schema, so endpoints use your object and field names directly — no opaque IDs.
لا توجد مرجعية ثابتة لواجهة برمجة التطبيقات الخاصة بـ Twenty. لكل مساحة عمل مخططها الخاص — عند إضافة كائن مخصص (مثل `Invoice`)، يحصل فورًا على نقاط نهاية REST وGraphQL مطابقة لتلك الخاصة بالكائنات المدمجة مثل `Company` أو `Person`. تُولَّد واجهة برمجة التطبيقات من المخطط، لذا تستخدم نقاط النهاية أسماء الكائنات والحقول لديك مباشرة — بدون معرّفات غامضة.
Your workspace-specific API documentation is available under **Settings → API & Webhooks** after creating an API key. It includes an interactive playground where you can execute real calls against your data.
وثائق واجهة برمجة التطبيقات الخاصة بمساحة عملك متاحة ضمن **الإعدادات → واجهات برمجة التطبيقات وخطافات الويب** بعد إنشاء مفتاح API. يتضمن ساحة تجريبية تفاعلية يمكنك من خلالها تنفيذ استدعاءات حقيقية على بياناتك.
## Two APIs
## واجهتا برمجة تطبيقات
**Core API** — `/rest/` and `/graphql/`
**واجهة برمجة التطبيقات الأساسية** — `/rest/` و`/graphql/`
CRUD on records: People, Companies, Opportunities, your custom objects. Query, filter, traverse relations.
عمليات CRUD على السجلات: الأشخاص، الشركات، الفرص، وكائناتك المخصصة. استعلام، تصفية، والتنقل عبر العلاقات.
**Metadata API** — `/rest/metadata/` and `/metadata/`
**واجهة برمجة تطبيقات البيانات الوصفية** — `/rest/metadata/` و`/metadata/`
Schema management: create/modify/delete objects, fields, and relations. This is how you programmatically change your data model.
إدارة المخطط: إنشاء/تعديل/حذف الكائنات والحقول والعلاقات. هذه هي الطريقة لتغيير نموذج بياناتك برمجيًا.
Both are available as REST and GraphQL. GraphQL adds batch upserts and the ability to traverse relations in a single query. Same underlying data either way.
كلاهما متاحان عبر REST وGraphQL. تضيف GraphQL عمليات upsert على دفعات وإمكانية التنقل عبر العلاقات في استعلام واحد. نفس البيانات الأساسية بأي من الطريقتين.
## Base URLs
## عناوين URL الأساسية
| البيئة | عنوان URL الأساسي |
| ----------- | ------------------------- |
| Cloud | `https://api.twenty.com/` |
| Self-Hosted | `https://{your-domain}/` |
| البيئة | عنوان URL الأساسي |
| ----------------- | ------------------------- |
| السحابة | `https://api.twenty.com/` |
| الاستضافة الذاتية | `https://{your-domain}/` |
## المصادقة
@@ -37,19 +37,19 @@ Both are available as REST and GraphQL. GraphQL adds batch upserts and the abili
Authorization: Bearer YOUR_API_KEY
```
Create an API key in **Settings → API & Webhooks → + Create key**. Copy it immediately — it's shown once. Keys can be scoped to a specific role under **Settings → Roles → Assignment tab** to limit what they can access.
أنشئ مفتاح API من **الإعدادات → واجهات برمجة التطبيقات وخطافات الويب → + Create key**. انسخه فورًا — يُعرَض مرة واحدة فقط. يمكن تقييد نطاق المفاتيح بدور محدد ضمن **الإعدادات → الأدوار → علامة التبويب Assignment** للحد مما يمكنها الوصول إليه.
<VimeoEmbed videoId="928786722" title="إنشاء مفتاح API" />
For OAuth-based access (external apps acting on behalf of users), see [OAuth](/l/ar/developers/extend/oauth).
للوصول المعتمد على OAuth (تطبيقات خارجية تتصرف نيابةً عن المستخدمين)، راجع [OAuth](/l/ar/developers/extend/oauth).
## Batch operations
## عمليات الدفعات
Both REST and GraphQL support batching up to 60 records per request — create, update, or delete. GraphQL also supports batch upsert (create-or-update in one call) using plural names like `CreateCompanies`.
يدعم كلٌّ من REST وGraphQL التجميع لما يصل إلى 60 سجلًا لكل طلب — إنشاء أو تحديث أو حذف. كما تدعم GraphQL عملية upsert على دفعات (إنشاء-أو-تحديث في استدعاء واحد) باستخدام أسماء جمع مثل `CreateCompanies`.
## Rate limits
## حدود المعدل
| الحد | القيمة |
| ---------- | ------------------ |
| Requests | 100 per minute |
| Batch size | 60 سجل لكل استدعاء |
| الحد | القيمة |
| ---------- | ---------------------- |
| الطلبات | 100 استدعاء في الدقيقة |
| حجم الدفعة | 60 سجل لكل استدعاء |
@@ -1,14 +1,14 @@
---
title: الهيكلية
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: كيف تعمل تطبيقات Twenty — العزل، دورة الحياة، واللبنات الأساسية.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
تطبيقات Twenty هي حزم TypeScript توسّع مساحة عملك بكائنات مخصّصة، ومنطق، ومكوّنات واجهة مستخدم (UI)، وقدرات ذكاء اصطناعي. تعمل على منصة Twenty مع عزل كامل وضوابط الأذونات.
## How apps work
## كيف تعمل التطبيقات
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
التطبيق عبارة عن مجموعة من **الكيانات** يتم إعلانها باستخدام دوال `defineEntity()` من حزمة `twenty-sdk`. يكتشف SDK هذه التصريحات عبر تحليل AST وقت البناء وينتج **ملف بيان** — وصفًا كاملًا لما يضيفه تطبيقك إلى مساحة العمل.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**تنظيم الملفات متروك لك.** يعتمد اكتشاف الكيانات على AST — يعثر SDK على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. بنية المجلدات أعلاه هي اصطلاح وليست متطلبًا.
</Note>
## Entity types
## أنواع الكيانات
| كيان | الغرض | وثائق |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/ar/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/ar/developers/extend/apps/data-model) |
| **الكائن** | Custom data tables with fields | [Data Model](/l/ar/developers/extend/apps/data-model) |
| **الحقل** | Extend existing objects, define relations | [Data Model](/l/ar/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [الوظائف المنطقية](/l/ar/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/ar/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/ar/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/ar/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/ar/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/ar/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/ar/developers/extend/apps/layout) |
| كيان | الغرض | وثائق |
| ---------------------- | ------------------------------------------------------ | -------------------------------------------------------------- |
| **تطبيق** | هوية التطبيق، الأذونات، المتغيرات | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) |
| **دور** | مجموعات الأذونات للكائنات والحقول | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) |
| **الكائن** | جداول بيانات مخصّصة مع حقول | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) |
| **الحقل** | توسيع الكائنات الموجودة، تعريف العلاقات | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) |
| **دالة منطقية** | TypeScript على جانب الخادم مع مشغّلات | [الوظائف المنطقية](/l/ar/developers/extend/apps/logic-functions) |
| **مكوّن أمامي** | واجهة مستخدم React معزولة داخل صفحة Twenty. | [المكوّنات الأمامية](/l/ar/developers/extend/apps/front-components) |
| **مهارة** | تعليمات قابلة لإعادة الاستخدام لوكلاء الذكاء الاصطناعي | [المهارات والوكلاء](/l/ar/developers/extend/apps/skills-and-agents) |
| **وكيل** | مساعدو الذكاء الاصطناعي بموجهات مخصّصة | [المهارات والوكلاء](/l/ar/developers/extend/apps/skills-and-agents) |
| **عرض** | عروض قوائم السجلات المكوّنة مسبقًا | [التخطيط](/l/ar/developers/extend/apps/layout) |
| **عنصر قائمة التنقّل** | عناصر الشريط الجانبي المخصّصة | [التخطيط](/l/ar/developers/extend/apps/layout) |
| **تخطيط الصفحة** | علامات تبويب وعناصر واجهة مخصّصة لصفحة السجل | [التخطيط](/l/ar/developers/extend/apps/layout) |
## Sandboxing
## العزل
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **الدوال المنطقية** تعمل في عمليات Node.js معزولة على الخادم. لا تصل إلى البيانات إلا عبر عميل API مضبوط الأنواع، ومقيَّد بأذونات دور التطبيق.
* **المكوّنات الأمامية** تعمل ضمن Web Workers باستخدام Remote DOM — معزولة عن الصفحة الرئيسية لكنها تعرض عناصر DOM الأصلية (وليس iframes). تتواصل مع Twenty عبر واجهة API للمضيف تعتمد تمرير الرسائل.
* **الأذونات** تُطبَّق على مستوى واجهة API. يُشتق رمز وقت التشغيل (`TWENTY_APP_ACCESS_TOKEN`) من الدور المعرَّف في `defineApplication()`.
## App lifecycle
## دورة حياة التطبيق
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/ar/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — يراقب ملفات المصدر لديك ويزامن التغييرات مباشرةً إلى خادم Twenty متصل. يُعاد توليد عميل API مضبوط الأنواع تلقائيًا عند تغيّر المخطط.
* **`yarn twenty build`** — يجمّع TypeScript، ويضمّن الدوال المنطقية والمكوّنات الأمامية باستخدام esbuild، وينتج ملف بيان.
* **خطّافات ما قبل/ما بعد التثبيت** — دوال منطقية اختيارية تعمل أثناء التثبيت. راجع [الدوال المنطقية](/l/ar/developers/extend/apps/logic-functions) للحصول على التفاصيل.
## الخطوات التالية
<CardGroup cols={٢}>
<Card title="نموذج البيانات" icon="database" href="/l/ar/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
عرّف الكائنات والحقول والأدوار والعلاقات.
</Card>
<Card title="الوظائف المنطقية" icon="bolt" href="/l/ar/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
دوال على جانب الخادم مع HTTP وcron ومشغّلات الأحداث.
</Card>
<Card title="المكوّنات الأمامية" icon="window-maximize" href="/l/ar/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
مكوّنات React معزولة داخل واجهة مستخدم Twenty.
</Card>
<Card title="التخطيط" icon="table-columns" href="/l/ar/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
العروض، وعناصر التنقّل، وتخطيطات صفحات السجل.
</Card>
<Card title="المهارات والوكلاء" icon="robot" href="/l/ar/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
مهارات ووكلاء ذكاء اصطناعي بموجهات مخصّصة.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/ar/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI والاختبار" icon="terminal" href="/l/ar/developers/extend/apps/cli-and-testing">
أوامر CLI، والاختبار، والأصول، والوحدات البعيدة، وCI.
</Card>
<Card title="النشر" icon="rocket" href="/l/ar/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
انشر إلى خادم أو انشر في السوق.
</Card>
</CardGroup>
@@ -1,6 +1,6 @@
---
title: المكوّنات الأمامية
description: Build React components that render inside Twenty's UI with sandboxed isolation.
description: أنشئ مكونات React تُعرَض داخل واجهة مستخدم Twenty ضمن بيئة معزولة (sandbox).
icon: window-maximize
---
@@ -1,23 +1,23 @@
---
title: التخطيط
description: Define views, navigation menu items, and page layouts to shape how your app appears in Twenty.
description: عرّف طرق العرض، وعناصر قائمة التنقّل، وتخطيطات الصفحات لتشكيل كيفية ظهور تطبيقك في Twenty.
icon: table-columns
---
Layout entities control how your app surfaces inside Twenty's UI — what lives in the sidebar, which saved views ship with the app, and how a record detail page is arranged.
تتحكّم كيانات التخطيط في كيفية ظهور تطبيقك داخل واجهة مستخدم Twenty — ما الذي يوجد في الشريط الجانبي، وأي العروض المحفوظة تأتي مع التطبيق، وكيف يتم ترتيب صفحة تفاصيل السجل.
## Layout concepts
## مفاهيم التخطيط
| Concept | What it controls | كيان |
| ------------------------ | --------------------------------------------------------------------------------- | -------------------------- |
| **View** | A saved list configuration for an object — visible fields, order, filters, groups | `defineView` |
| **Navigation Menu Item** | An entry in the left sidebar that links to a view or an external URL | `defineNavigationMenuItem` |
| **Page Layout** | The tabs and widgets that make up a record's detail page | `definePageLayout` |
| المفهوم | ما الذي يتحكّم فيه | كيان |
| ---------------------- | ------------------------------------------------------------------------------- | -------------------------- |
| **عرض** | تكوين قائمة محفوظة لكائن — الحقول المرئية، والترتيب، وعوامل التصفية، والمجموعات | `defineView` |
| **عنصر قائمة التنقّل** | عنصر في الشريط الجانبي الأيسر يرتبط بعرض أو بعنوان URL خارجي | `defineNavigationMenuItem` |
| **تخطيط الصفحة** | علامات التبويب وعناصر الواجهة التي تشكّل صفحة تفاصيل السجل | `definePageLayout` |
Views, navigation items, and page layouts reference each other by `universalIdentifier`:
تشير العروض، وعناصر التنقّل، وتخطيطات الصفحات إلى بعضها البعض عبر `universalIdentifier`:
* A **navigation menu item** of type `VIEW` points at a `defineView` identifier, so the sidebar link opens that saved view.
* A **page layout** of type `RECORD_PAGE` targets an object and can embed [front components](/l/ar/developers/extend/apps/front-components) inside its tabs as widgets.
* يشير **عنصر قائمة التنقّل** من النوع `VIEW` إلى معرّف `defineView`، بحيث يفتح رابط الشريط الجانبي ذلك العرض المحفوظ.
* يستهدف **تخطيط الصفحة** من النوع `RECORD_PAGE` كائنًا ويمكنه تضمين [مكوّنات الواجهة الأمامية](/l/ar/developers/extend/apps/front-components) داخل علامات التبويب الخاصة به بوصفها عناصر واجهة.
<AccordionGroup>
<Accordion title="defineView" description="تعريف العروض المحفوظة للكائنات">
@@ -1,6 +1,6 @@
---
title: المهارات والوكلاء
description: Define AI skills and agents for your app.
description: عرّف مهارات ووكلاء الذكاء الاصطناعي لتطبيقك.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. الميزة تعمل لكنها لا تزال قيد التطور.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
يمكن للتطبيقات تعريف قدرات ذكاء اصطناعي تعمل داخل مساحة العمل — تعليمات مهارات قابلة لإعادة الاستخدام ووكلاء بموجهات نظام مخصّصة.
<AccordionGroup>
<Accordion title="defineSkill" description="عرّف مهارات وكلاء الذكاء الاصطناعي">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: المفتاح
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: تدفق رمز التفويض مع PKCE وبيانات اعتماد العميل للوصول من خادم إلى خادم.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
تُطبِّق Twenty بروتوكول OAuth 2.0 باستخدام رمز التفويض + PKCE للتطبيقات المواجهة للمستخدم، وبيانات اعتماد العميل للوصول من خادم إلى خادم. يُجرى تسجيل العملاء ديناميكيًا عبر [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — دون إعداد يدوي في لوحة التحكم.
## When to Use OAuth
## متى تستخدم OAuth
| السيناريو | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/ar/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/ar/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| السيناريو | طريقة المصادقة |
| -------------------------------------------- | ------------------------------------------------------------------------------------ |
| البرامج النصية الداخلية، والأتمتة | [مفتاح API](/l/ar/developers/extend/api#authentication) |
| تطبيق خارجي يعمل نيابةً عن مستخدم | **OAuth — رمز التفويض** |
| من خادم إلى خادم، دون سياق مستخدم | **OAuth — بيانات اعتماد العميل** |
| تطبيق Twenty مع امتدادات واجهة المستخدم (UI) | [التطبيقات](/l/ar/developers/extend/apps/getting-started) (يتم التعامل مع OAuth تلقائيًا) |
## Register a Client
## تسجيل عميل
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
تدعم Twenty **التسجيل الديناميكي للعملاء** وفقًا لـ[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). لا حاجة إلى إعداد يدوي — سجّل برمجيًا:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**الاستجابة:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
احفظ `client_secret` بأمان — لا يمكن استرجاعه لاحقًا.
</Warning>
## النطاقات
| Scope | الوصول |
| --------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `profile` | Read the authenticated user's profile information |
| النطاق | الوصول |
| --------- | -------------------------------------------------------------- |
| `api` | إمكانية قراءة/كتابة كاملة لواجهات برمجة تطبيقات Core وMetadata |
| `profile` | قراءة معلومات ملف تعريف المستخدم المُصادَق عليه |
Request scopes as a space-separated string: `scope=api profile`
اطلب النطاقات كسلسلة مفصولة بمسافات: `scope=api profile`
## Authorization Code Flow
## تدفق رمز التفويض
Use this flow when your app acts on behalf of a Twenty user.
استخدم هذا التدفق عندما يعمل تطبيقك نيابةً عن مستخدم Twenty.
### 1. Redirect the user to authorize
### 1. أعد توجيه المستخدم للتفويض
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| المعلمة | مطلوب | الوصف |
| ----------------------- | -------- | ------------------------------------------------------------ |
| `client_id` | نعم | Your registered client ID |
| `response_type` | نعم | Must be `code` |
| `redirect_uri` | نعم | Must match a registered redirect URI |
| `scope` | لا | Space-separated scopes (defaults to `api`) |
| `الحالة` | مُوصى به | Random string to prevent CSRF attacks |
| `code_challenge` | مُوصى به | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | مُوصى به | Must be `S256` when using PKCE |
| المعلمة | مطلوب | الوصف |
| ----------------------- | -------- | -------------------------------------------------------- |
| `client_id` | نعم | معرّف العميل المسجّل الخاص بك |
| `response_type` | نعم | يجب أن يكون `code` |
| `redirect_uri` | نعم | يجب أن يطابق عنوان URI لإعادة التوجيه المسجّل |
| `scope` | لا | نطاقات مفصولة بمسافات (القيمة الافتراضية هي `api`) |
| `state` | مُوصى به | سلسلة عشوائية لمنع هجمات CSRF |
| `code_challenge` | مُوصى به | تحدّي PKCE (تجزئة SHA-256 لـ verifier، بترميز base64url) |
| `code_challenge_method` | مُوصى به | يجب أن تكون `S256` عند استخدام PKCE |
The user sees a consent screen and approves or denies access.
يرى المستخدم شاشة موافقة ويوافق على الوصول أو يرفضه.
### ٢. Handle the callback
### ٢. معالجة الاستدعاء المرتجع
After authorization, Twenty redirects back to your `redirect_uri`:
بعد التفويض، تعيد Twenty التوجيه إلى `redirect_uri` الخاص بك:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
تحقّق من أن قيمة `state` تطابق ما أرسلته.
### ٣. Exchange the code for tokens
### ٣. استبدِل الرمز بالرموز
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**الاستجابة:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. استخدم رمز الوصول
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. حدِّث عند انتهاء الصلاحية
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## تدفق بيانات اعتماد العميل
For server-to-server integrations with no user interaction:
لعمليات التكامل من خادم إلى خادم دون تفاعل مستخدم:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
الرمز المُعاد يمتلك وصولًا على مستوى مساحة العمل، وغير مرتبط بأي مستخدم محدّد.
## Server Discovery
## اكتشاف الخادم
Twenty publishes its OAuth configuration at a standard discovery endpoint:
تنشر Twenty إعدادات OAuth الخاصة بها عند نقطة اكتشاف قياسية:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
يعيد هذا جميع نقاط النهاية وأنواع المنح المدعومة والنطاقات والقدرات — وهو مفيد لبناء عملاء OAuth عامّين.
## API Endpoints Summary
## ملخص نقاط نهاية API
| نقطة النهاية | الغرض |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| نقطة النهاية | الغرض |
| ----------------------------------------- | -------------------------- |
| `/.well-known/oauth-authorization-server` | اكتشاف بيانات تعريف الخادم |
| `/oauth/register` | التسجيل الديناميكي للعميل |
| `/oauth/authorize` | تفويض المستخدم |
| `/oauth/token` | مبادلة الرموز وتحديثها |
| البيئة | عنوان URL الأساسي |
| --------------------- | ------------------------ |
| **السحابة** | `https://api.twenty.com` |
| **الاستضافة الذاتية** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth مقابل مفاتيح API
| | مفاتيح واجهة برمجة التطبيقات | OAuth |
| ------------------ | ---------------------------- | -------------------------------------- |
| **الإعداد** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **الأفضل لـ** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | يدوي | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | مفاتيح واجهة برمجة التطبيقات | OAuth |
| ------------------------ | -------------------------------- | ------------------------------------------ |
| **الإعداد** | إنشاء من الإعدادات | تسجيل عميل، وتنفيذ التدفق |
| **سياق المستخدم** | لا يوجد (على مستوى مساحة العمل) | أذونات مستخدم محدّد |
| **الأفضل لـ** | البرامج النصية، الأدوات الداخلية | تطبيقات خارجية، وتكاملات متعددة المستخدمين |
| **تدوير الرموز** | يدوي | تلقائي عبر رموز التحديث |
| **وصول محدود بالنطاقات** | وصول كامل إلى API | تفصيلي عبر النطاقات |
@@ -1,12 +1,12 @@
---
title: خطافات الويب
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: احصل على إشعار عند تغيّر السجلات — سيتم إرسال طلب HTTP POST إلى endpoint الخاص بك عند كل عملية إنشاء أو تحديث أو حذف.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
يقوم Twenty بإرسال طلب HTTP POST إلى URL الخاص بك كلما تم إنشاء سجل أو تحديثه أو حذفه. جميع أنواع الكائنات مشمولة، بما في ذلك الكائنات المخصصة.
## إنشاء خطاف ويب
@@ -38,7 +38,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
| linkToEntity | نص | الرابط إلى الكيان |
| معرف الكيان | نص | المعرف الفريد للكيان |
@@ -26,9 +26,9 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| -------- | ----------------- | ------------------------ |
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
| الخصائص | النوع | الوصف |
| ------- | ----------------- | ------------------------ |
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
@@ -40,7 +40,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
| معطل | قيمة منطقية | يقوم بتعطيل منتقى الأيقونات إذا تم تعيينه إلى `true` |
| عند التغيير | دالة | الدالة الارتجاعية التي تُفعل عندما يختار المستخدم أيقونة. يستقبل كائنًا يحتوي على الخصائص `iconKey` و `Icon` |
@@ -23,7 +23,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| ------------ | ----------- | --------------------------------------------------------------------------------- |
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
| onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط |
@@ -38,7 +38,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
@@ -35,7 +35,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
| روابط | مصفوفة | مصفوفة من الكائنات، يمثّل كلٌّ منها رابطًا في مسار التنقّل. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
@@ -30,7 +30,7 @@ export const MyComponent = () => {
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| الخصائص | النوع | الوصف |
| ------------- | ----- | ----------------------------------------------------------------- |
| الخطوة النشطة | رقم | مؤشر للخطوة النشطة حاليًا. هذا يحدد أي خطوة يجب إبرازها بشكل مرئي |
@@ -1,12 +1,12 @@
---
title: Commands
title: Příkazy
icon: terminal
description: Useful commands for developing Twenty.
description: Užitečné příkazy pro vývoj Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
Příkazy je možné spouštět z kořene repozitáře pomocí `npx nx`. Pro explicitní cílení použijte `npx nx run {project}:{command}`.
## Starting the App
## Spuštění aplikace
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## Databáze
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -22,7 +22,7 @@ npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
## Lintování
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Kontrola typů
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## Sestavení
```bash
npx nx build twenty-shared # Must be built first
@@ -1,14 +1,14 @@
---
title: Stylová příručka
icon: paintbrush
description: Code conventions and best practices for contributing to Twenty.
description: Konvence kódu a osvědčené postupy pro přispívání do Twenty.
---
## React
### Functional components only
### Pouze funkcionální komponenty
Always use TSX functional components with named exports.
Vždy používejte funkcionální komponenty TSX s pojmenovanými exporty.
```tsx
// ❌ Bad
@@ -25,7 +25,7 @@ export function MyComponent() {
### Vlastnosti
Create a type named `{ComponentName}Props`. Use destructuring. Don't use `React.FC`.
Vytvořte typ s názvem `{ComponentName}Props`. Používejte destrukturalizaci. Nepoužívejte `React.FC`.
```tsx
type MyComponentProps = {
@@ -35,7 +35,7 @@ type MyComponentProps = {
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### No single-variable prop spreading
### Nepoužívejte prop spreading jediné proměnné
```tsx
// ❌ Bad
@@ -47,7 +47,7 @@ const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1,
## Správa stavu
### Jotai atoms for global state
### Atomy Jotai pro globální stav
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
@@ -59,16 +59,16 @@ export const myAtomState = createAtomState<string>({
});
```
* Prefer atoms over prop drilling
* Don't use `useRef` for stateuse `useState` or atoms
* Use atom families and selectors for lists
* Upřednostňujte atomy před prop drillingem
* Nepoužívejte `useRef` pro stavpoužijte `useState` nebo atomy
* Používejte rodiny atomů a selektory pro seznamy
### Avoid unnecessary re-renders
### Vyhněte se zbytečnému opakovanému vykreslování
* Extract `useEffect` and data fetching into sibling sidecar components
* Prefer event handlers (`handleClick`, `handleChange`) over `useEffect`
* Don't use `React.memo()` — fix the root cause instead
* Limit `useCallback` / `useMemo` usage
* Přesuňte `useEffect` a načítání dat do sesterských sidecar komponent
* Upřednostňujte obslužné funkce událostí (`handleClick`, `handleChange`) před `useEffect`
* Nepoužívejte `React.memo()` — místo toho opravte kořenovou příčinu
* Omezte používání `useCallback` / `useMemo`
```tsx
// ❌ Bad — useEffect in the same component causes re-renders
@@ -94,11 +94,11 @@ export const Page = () => {
## TypeScript
* **`type` over `interface`** — more flexible, easier to compose
* **String literals over enums** — except for GraphQL codegen enums and internal library APIs
* **No `any`** — strict TypeScript enforced
* **No type imports** — use regular imports (enforced by Oxlint `typescript/consistent-type-imports`)
* **Use [Zod](https://github.com/colinhacks/zod)** for runtime validation of untyped objects
* **`type` místo `interface`** — flexibilnější, lépe kombinovatelný
* **Řetězcové literály místo výčtů** — s výjimkou enumů GraphQL codegenu a interních API knihoven
* **Žádné `any`** — vynucený přísný TypeScript
* **Žádné importy typů** — používejte běžné importy (vynuceno nástrojem Oxlint `typescript/consistent-type-imports`)
* **Používejte [Zod](https://github.com/colinhacks/zod)** pro runtime validaci netypovaných objektů
## JavaScript
@@ -112,17 +112,17 @@ onClick?.();
## Pojmenovávání
* **Variables**: camelCase, descriptive (`email` not `value`, `fieldMetadata` not `fm`)
* **Constants**: SCREAMING_SNAKE_CASE
* **Types/Classes**: PascalCase
* **Files/directories**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Event handlers**: `handleClick` (not `onClick` for the handler function)
* **Component props**: prefix with component name (`ButtonProps`)
* **Styled components**: prefix with `Styled` (`StyledTitle`)
* **Proměnné**: camelCase, popisné (`email` nikoli `value`, `fieldMetadata` nikoli `fm`)
* **Konstanty**: SCREAMING_SNAKE_CASE
* **Typy/Třídy**: PascalCase
* **Soubory/adresáře**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Obslužné funkce událostí**: `handleClick` (nikoli `onClick` pro obslužnou funkci)
* **Vlastnosti komponenty (props)**: předponu tvoří název komponenty (`ButtonProps`)
* **Stylované komponenty**: předpona `Styled` (`StyledTitle`)
## Styling
Use [Linaria](https://github.com/callstack/linaria) styled components. Use theme values — avoid hardcoded `px`, `rem`, or colors.
Používejte stylované komponenty [Linaria](https://github.com/callstack/linaria). Používejte hodnoty z tématu — vyhněte se napevno zadaným `px`, `rem` nebo barvám.
```tsx
// ❌ Bad
@@ -142,7 +142,7 @@ const StyledButton = styled.button`
## Importy
Use aliases instead of relative paths:
Používejte aliasy místo relativních cest:
```tsx
// ❌ Bad
@@ -153,7 +153,7 @@ import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## Folder Structure
## Struktura složek
```
front
@@ -171,6 +171,6 @@ front
└── ui/ # Reusable UI components (display, input, feedback, ...)
```
* Modules can import from other modules, but `ui/` should stay dependency-free
* Use `internal/` subfolders for module-private code
* Components under 300 lines, services under 500 lines
* Moduly mohou importovat z jiných modulů, ale `ui/` by mělo zůstat bez závislostí
* Používejte podadresáře `internal/` pro interní kód modulu
* Komponenty do 300 řádků, služby do 500 řádků
@@ -1,14 +1,14 @@
---
title: Architektura
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Jak fungují aplikace Twenty — sandboxing, životní cyklus a stavební bloky.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Aplikace Twenty jsou balíčky TypeScriptu, které rozšiřují váš pracovní prostor o vlastní objekty, logiku, komponenty UI a funkce AI. Běží na platformě Twenty s plnou izolací (sandboxingem) a řízením oprávnění.
## How apps work
## Jak aplikace fungují
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Aplikace je kolekce **entit** deklarovaných pomocí funkcí `defineEntity()` z balíčku `twenty-sdk`. SDK tyto deklarace detekuje pomocí analýzy AST při sestavení a vytváří **manifest** — úplný popis toho, co vaše aplikace přidává do pracovního prostoru.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**Uspořádání souborů je na vás.** Detekce entit je založená na AST — SDK najde volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. Výše uvedená struktura složek je konvence, nikoli požadavek.
</Note>
## Entity types
## Typy entit
| Entita | Účel | Dokumentace |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/cs/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/cs/developers/extend/apps/data-model) |
| **Objekt** | Custom data tables with fields | [Data Model](/l/cs/developers/extend/apps/data-model) |
| **Pole** | Extend existing objects, define relations | [Data Model](/l/cs/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Logické funkce](/l/cs/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/cs/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/cs/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/cs/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/cs/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/cs/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/cs/developers/extend/apps/layout) |
| Entita | Účel | Dokumentace |
| ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------ |
| **Aplikace** | Identita aplikace, oprávnění, proměnné | [Datový model](/l/cs/developers/extend/apps/data-model) |
| **Role** | Sady oprávnění pro objekty a pole | [Datový model](/l/cs/developers/extend/apps/data-model) |
| **Objekt** | Vlastní datové tabulky s poli | [Datový model](/l/cs/developers/extend/apps/data-model) |
| **Pole** | Rozšíření existujících objektů, definice rela | [Datový model](/l/cs/developers/extend/apps/data-model) |
| **Logická funkce** | TypeScript na straně serveru se spouštěči | [Logické funkce](/l/cs/developers/extend/apps/logic-functions) |
| **Frontendová komponenta** | Izolované React UI na stránce Twenty | [Frontendové komponenty](/l/cs/developers/extend/apps/front-components) |
| **Dovednost** | Znovupoužitelné pokyny pro AI agenty | [Dovednosti a agenti](/l/cs/developers/extend/apps/skills-and-agents) |
| **Agent** | AI asistenti s vlastními prompty | [Dovednosti a agenti](/l/cs/developers/extend/apps/skills-and-agents) |
| **Pohled** | Předkonfigurovaná zobrazení seznamu záznamů | [Rozvržení](/l/cs/developers/extend/apps/layout) |
| **Položka navigační nabídky** | Vlastní položky postranního panelu | [Rozvržení](/l/cs/developers/extend/apps/layout) |
| **Rozvržení stránky** | Vlastní karty a widgety na stránce záznamu | [Rozvržení](/l/cs/developers/extend/apps/layout) |
## Sandboxing
## Izolace (sandboxing)
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Logic funkce** běží v izolovaných procesech Node.js na serveru. K datům přistupují pouze prostřednictvím typovaného klienta API, a to v rozsahu oprávnění role aplikace.
* **Frontendové komponenty** běží ve Web Workerech s využitím Remote DOM — jsou oddělené od hlavní stránky, ale vykreslují nativní prvky DOM (nikoli iframy). Komunikují s Twenty prostřednictvím hostitelského API pro předávání zpráv.
* **Oprávnění** jsou vynucována na úrovni API. Běhový token (`TWENTY_APP_ACCESS_TOKEN`) je odvozen z role definované v `defineApplication()`.
## App lifecycle
## Životní cyklus aplikace
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/cs/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — sleduje vaše zdrojové soubory a průběžně synchronizuje změny s připojeným serverem Twenty. Typovaný klient API se při změně schématu automaticky znovu vygeneruje.
* **`yarn twenty build`** — zkompiluje TypeScript, zabalí logic funkce a frontendové komponenty pomocí esbuild a vytvoří manifest.
* **Pre/post-install hooks** — volitelné logic funkce, které běží během instalace. Podrobnosti najdete v [Logických funkcích](/l/cs/developers/extend/apps/logic-functions).
## Další kroky
<CardGroup cols={2}>
<Card title="Datový model" icon="database" href="/l/cs/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Definujte objekty, pole, role a relace.
</Card>
<Card title="Logické funkce" icon="bolt" href="/l/cs/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
Funkce na straně serveru s HTTP, cron a událostními spouštěči.
</Card>
<Card title="Frontendové komponenty" icon="window-maximize" href="/l/cs/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Izolované komponenty Reactu v uživatelském rozhraní Twenty.
</Card>
<Card title="Rozvržení" icon="table-columns" href="/l/cs/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Pohledy, položky navigace a rozvržení stránek záznamů.
</Card>
<Card title="Dovednosti a agenti" icon="robot" href="/l/cs/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
AI dovednosti a agenti s vlastními prompty.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/cs/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI a testování" icon="terminal" href="/l/cs/developers/extend/apps/cli-and-testing">
Příkazy CLI, testování, prostředky, vzdálené zdroje a CI.
</Card>
<Card title="Publikování" icon="rocket" href="/l/cs/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Nasaďte na server nebo publikujte na tržišti.
</Card>
</CardGroup>
@@ -1,23 +1,23 @@
---
title: Rozvržení
description: Define views, navigation menu items, and page layouts to shape how your app appears in Twenty.
description: Definujte pohledy, položky navigační nabídky a rozvržení stránek, abyste utvářeli, jak se vaše aplikace zobrazuje v Twenty.
icon: table-columns
---
Layout entities control how your app surfaces inside Twenty's UI — what lives in the sidebar, which saved views ship with the app, and how a record detail page is arranged.
Prvky rozvržení řídí, jak se vaše aplikace zobrazuje v uživatelském rozhraní Twenty — co je v postranním panelu, které uložené pohledy jsou součástí aplikace a jak je uspořádána stránka s podrobnostmi záznamu.
## Layout concepts
## Pojmy rozvržení
| Concept | What it controls | Entita |
| ------------------------ | --------------------------------------------------------------------------------- | -------------------------- |
| **View** | A saved list configuration for an object — visible fields, order, filters, groups | `defineView` |
| **Navigation Menu Item** | An entry in the left sidebar that links to a view or an external URL | `defineNavigationMenuItem` |
| **Page Layout** | The tabs and widgets that make up a record's detail page | `definePageLayout` |
| Pojem | Co řídí | Entita |
| ----------------------------- | ------------------------------------------------------------------------------ | -------------------------- |
| **Pohled** | Uložené nastavení seznamu pro objekt — viditelná pole, pořadí, filtry, skupiny | `defineView` |
| **Položka navigační nabídky** | Položka v levém postranním panelu, která odkazuje na pohled nebo externí URL | `defineNavigationMenuItem` |
| **Rozvržení stránky** | Karty a widgety, které tvoří stránku s podrobnostmi záznamu | `definePageLayout` |
Views, navigation items, and page layouts reference each other by `universalIdentifier`:
Pohledy, položky navigační nabídky a rozvržení stránek se na sebe odkazují pomocí `universalIdentifier`:
* A **navigation menu item** of type `VIEW` points at a `defineView` identifier, so the sidebar link opens that saved view.
* A **page layout** of type `RECORD_PAGE` targets an object and can embed [front components](/l/cs/developers/extend/apps/front-components) inside its tabs as widgets.
* Položka **navigační nabídky** typu `VIEW` odkazuje na identifikátor `defineView`, takže odkaz v postranním panelu otevře daný uložený pohled.
* **Rozvržení stránky** typu `RECORD_PAGE` cílí na objekt a může vkládat [front components](/l/cs/developers/extend/apps/front-components) do svých karet jako widgety.
<AccordionGroup>
<Accordion title="defineView" description="Definujte uložená zobrazení pro objekty">
@@ -1,6 +1,6 @@
---
title: Dovednosti a agenti
description: Define AI skills and agents for your app.
description: Definujte dovednosti a agenty AI pro svou aplikaci.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. Funkce funguje, ale stále se vyvíjí.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Aplikace mohou definovat schopnosti AI, které fungují přímo v pracovním prostoru — znovupoužitelné pokyny pro dovednosti a agenty s vlastními systémovými prompty.
<AccordionGroup>
<Accordion title="defineSkill" description="Definujte dovednosti agenta AI">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: klíč
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: Tok s autorizačním kódem s PKCE a přihlašovacími údaji klienta pro přístup server-to-server.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty implementuje OAuth 2.0 s autorizačním kódem + PKCE pro aplikace pro uživatele a přihlašovací údaje klienta pro přístup server-to-server. Klienti se registrují dynamicky přes [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — žádné ruční nastavení v dashboardu.
## When to Use OAuth
## Kdy použít OAuth
| Scénář | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/cs/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/cs/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Scénář | Metoda ověřování |
| ---------------------------------------------------- | -------------------------------------------------------------------------------- |
| Interní skripty, automatizace | [Klíč API](/l/cs/developers/extend/api#authentication) |
| Externí aplikace jednající jménem uživatele | **OAuth — autorizační kód** |
| Server-to-server, bez uživatelského kontextu | **OAuth — přihlašovací údaje klienta** |
| Aplikace Twenty s rozšířeními uživatelského rozhraní | [Aplikace](/l/cs/developers/extend/apps/getting-started) (OAuth je řešen automaticky) |
## Register a Client
## Registrace klienta
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
Twenty podporuje **dynamickou registraci klienta** podle [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). Není potřeba žádné ruční nastavení — registrujte programově:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Odpověď:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
Uložte `client_secret` bezpečně — později jej nelze získat zpět.
</Warning>
## Oprávnění
| Scope | Přístup |
| -------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `profil` | Read the authenticated user's profile information |
| Oprávnění | Přístup |
| --------- | ----------------------------------------------------- |
| `api` | Úplný přístup pro čtení i zápis k API Core a Metadata |
| `profile` | Čtení informací o profilu ověřeného uživatele |
Request scopes as a space-separated string: `scope=api profile`
Vyžádejte oprávnění jako řetězec oddělený mezerami: `scope=api profile`
## Authorization Code Flow
## Tok s autorizačním kódem
Use this flow when your app acts on behalf of a Twenty user.
Tento tok použijte, když vaše aplikace jedná jménem uživatele Twenty.
### 1. Redirect the user to authorize
### 1. Přesměrujte uživatele k autorizaci
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Parametr | Povinné | Popis |
| ----------------------- | ---------- | ------------------------------------------------------------ |
| `client_id` | Ano | Your registered client ID |
| `response_type` | Ano | Must be `code` |
| `redirect_uri` | Ano | Must match a registered redirect URI |
| `scope` | Ne | Space-separated scopes (defaults to `api`) |
| `stav` | Doporučeno | Random string to prevent CSRF attacks |
| `code_challenge` | Doporučeno | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Doporučeno | Must be `S256` when using PKCE |
| Parametr | Povinné | Popis |
| ----------------------- | ---------- | -------------------------------------------------------------- |
| `client_id` | Ano | Vaše registrované ID klienta |
| `response_type` | Ano | Musí být `code` |
| `redirect_uri` | Ano | Musí odpovídat registrované adrese URI pro přesměrování |
| `scope` | Ne | Oprávnění oddělená mezerami (výchozí je `api`) |
| `state` | Doporučeno | Náhodný řetězec k prevenci útoků CSRF |
| `code_challenge` | Doporučeno | Výzva PKCE (hash SHA-256 z verifieru, kódovaný jako base64url) |
| `code_challenge_method` | Doporučeno | Při použití PKCE musí být `S256` |
The user sees a consent screen and approves or denies access.
Uživatel uvidí souhlasovou obrazovku a přístup schválí nebo zamítne.
### 2. Handle the callback
### 2. Zpracujte callback
After authorization, Twenty redirects back to your `redirect_uri`:
Po autorizaci Twenty přesměruje zpět na vaše `redirect_uri`:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
Ověřte, že `state` odpovídá tomu, co jste poslali.
### 3. Exchange the code for tokens
### 3. Vyměňte kód za tokeny
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Odpověď:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Použijte přístupový token
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Obnovte po vypršení platnosti
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## Tok s přihlašovacími údaji klienta
For server-to-server integrations with no user interaction:
Pro integrace server-to-server bez interakce uživatele:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
Vrácený token má přístup na úrovni pracovního prostoru, není vázán na žádného konkrétního uživatele.
## Server Discovery
## Zjišťování serveru
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty zveřejňuje svou konfiguraci OAuth na standardním koncovém bodu pro zjišťování:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Vrací všechny koncové body, podporované typy grantů, oprávnění a možnosti — užitečné pro tvorbu obecných klientů OAuth.
## API Endpoints Summary
## Přehled koncových bodů API
| Koncový bod | Účel |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Koncový bod | Účel |
| ----------------------------------------- | ---------------------------- |
| `/.well-known/oauth-authorization-server` | Zjišťování metadat serveru |
| `/oauth/register` | Dynamic registrace klienta |
| `/oauth/authorize` | Autorizace uživatele |
| `/oauth/token` | Výměna a obnovení tokenu |
| Prostředí | Základní URL |
| ------------------- | ------------------------ |
| **Cloud** | `https://api.twenty.com` |
| **Vlastní hosting** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth vs klíče API
| | API Klíče | OAuth |
| ------------------ | ----------------------- | -------------------------------------- |
| **Nastavení** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Vhodné pro** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Ruční | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API Klíče | OAuth |
| --------------------------- | ------------------------------------- | ------------------------------------------- |
| **Nastavení** | Generovat v Nastavení | Zaregistrovat klienta, implementovat tok |
| **Uživatelský kontext** | Žádný (na úrovni pracovního prostoru) | Oprávnění konkrétního uživatele |
| **Vhodné pro** | Skripty, interní nástroje | Externí aplikace, víceuživatelské integrace |
| **Rotace tokenů** | Ruční | Automaticky prostřednictvím refresh tokenů |
| **Přístup podle oprávnění** | Plný přístup k API | Jemně odstupňovaný pomocí oprávnění |
@@ -1,12 +1,12 @@
---
title: Webhooky
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Dostávejte upozornění při změnách záznamů — HTTP POST na váš koncový bod při každém vytvoření, aktualizaci nebo smazání.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Twenty odešle HTTP POST na vaši adresu URL pokaždé, když je záznam vytvořen, aktualizován nebo smazán. Všechny typy objektů jsou podporovány, včetně vlastních objektů.
## Vytvořit Webhook
@@ -1,6 +1,6 @@
---
title: Řešení potíží
icon: wrench
icon: klíč
---
## Řešení potíží
@@ -1,5 +1,5 @@
---
title: Záznamové stránky},{
title: Stránky záznamů
description: Přizpůsobte rozvržení jednotlivých stránek detailu záznamu pomocí karet a widgetů.
---
@@ -1,12 +1,12 @@
---
title: Commands
title: Befehle
icon: terminal
description: Useful commands for developing Twenty.
description: Nützliche Befehle für die Entwicklung von Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
Befehle können vom Repository-Stammverzeichnis mit `npx nx` ausgeführt werden. Verwende `npx nx run {project}:{command}` für eine explizite Zielangabe.
## Starting the App
## Die App starten
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## Datenbank
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Typprüfung
```bash
npx nx typecheck twenty-front
@@ -1,14 +1,14 @@
---
title: Styleguide
icon: paintbrush
description: Code conventions and best practices for contributing to Twenty.
description: Code-Konventionen und bewährte Verfahren für Beiträge zu Twenty.
---
## React
### Functional components only
### Ausschließlich funktionale Komponenten
Always use TSX functional components with named exports.
Verwenden Sie immer TSX-Funktionskomponenten mit benannten Exporten.
```tsx
// ❌ Bad
@@ -25,7 +25,7 @@ export function MyComponent() {
### Props
Create a type named `{ComponentName}Props`. Use destructuring. Don't use `React.FC`.
Erstellen Sie einen Typ namens `{ComponentName}Props`. Verwenden Sie Destrukturierung. Verwenden Sie `React.FC` nicht.
```tsx
type MyComponentProps = {
@@ -35,7 +35,7 @@ type MyComponentProps = {
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### No single-variable prop spreading
### Kein Prop-Spreading mit einer einzelnen Variablen
```tsx
// ❌ Bad
@@ -47,7 +47,7 @@ const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1,
## Zustandsverwaltung
### Jotai atoms for global state
### Jotai-Atome für globalen Zustand
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
@@ -59,16 +59,16 @@ export const myAtomState = createAtomState<string>({
});
```
* Prefer atoms over prop drilling
* Don't use `useRef` for stateuse `useState` or atoms
* Use atom families and selectors for lists
* Bevorzugen Sie Atome gegenüber Prop-Drilling
* Verwenden Sie `useRef` nicht für den Zustandverwenden Sie `useState` oder Atome
* Verwenden Sie Atomfamilien und Selektoren für Listen
### Avoid unnecessary re-renders
### Vermeiden Sie unnötige Re-Renders
* Extract `useEffect` and data fetching into sibling sidecar components
* Prefer event handlers (`handleClick`, `handleChange`) over `useEffect`
* Don't use `React.memo()` — fix the root cause instead
* Limit `useCallback` / `useMemo` usage
* Lagern Sie `useEffect` und das Daten-Fetching in gleichrangige Sidecar-Komponenten aus
* Bevorzugen Sie Event-Handler (`handleClick`, `handleChange`) gegenüber `useEffect`
* Verwenden Sie `React.memo()` nicht — beheben Sie stattdessen die Grundursache
* Beschränken Sie die Nutzung von `useCallback`/`useMemo`
```tsx
// ❌ Bad — useEffect in the same component causes re-renders
@@ -94,11 +94,11 @@ export const Page = () => {
## TypeScript
* **`type` over `interface`** — more flexible, easier to compose
* **String literals over enums** — except for GraphQL codegen enums and internal library APIs
* **No `any`** — strict TypeScript enforced
* **No type imports** — use regular imports (enforced by Oxlint `typescript/consistent-type-imports`)
* **Use [Zod](https://github.com/colinhacks/zod)** for runtime validation of untyped objects
* **`type` statt `interface`** — flexibler, leichter zu kombinieren
* **String-Literale statt Enums** — außer für GraphQL-Codegen-Enums und interne Bibliotheks-APIs
* **Kein `any`** — striktes TypeScript wird durchgesetzt
* **Keine Type-Imports** — verwenden Sie reguläre Imports (erzwungen durch Oxlint `typescript/consistent-type-imports`)
* **Verwenden Sie [Zod](https://github.com/colinhacks/zod)** für die Laufzeitvalidierung ungetypter Objekte
## JavaScript
@@ -112,17 +112,17 @@ onClick?.();
## Namensgebung
* **Variables**: camelCase, descriptive (`email` not `value`, `fieldMetadata` not `fm`)
* **Constants**: SCREAMING_SNAKE_CASE
* **Types/Classes**: PascalCase
* **Files/directories**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Event handlers**: `handleClick` (not `onClick` for the handler function)
* **Component props**: prefix with component name (`ButtonProps`)
* **Styled components**: prefix with `Styled` (`StyledTitle`)
* **Variablen**: camelCase, aussagekräftig (`email` statt `value`, `fieldMetadata` statt `fm`)
* **Konstanten**: SCREAMING_SNAKE_CASE
* **Typen/Klassen**: PascalCase
* **Dateien/Verzeichnisse**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Event-Handler**: `handleClick` (nicht `onClick` für die Handler-Funktion)
* **Komponenten-Props**: mit dem Komponentennamen präfixieren (`ButtonProps`)
* **Styled Components**: mit `Styled` präfixieren (`StyledTitle`)
## Styling
Use [Linaria](https://github.com/callstack/linaria) styled components. Use theme values — avoid hardcoded `px`, `rem`, or colors.
Verwenden Sie [Linaria](https://github.com/callstack/linaria) Styled Components. Verwenden Sie Theme-Werte — vermeiden Sie hartkodierte `px`, `rem` oder Farben.
```tsx
// ❌ Bad
@@ -142,7 +142,7 @@ const StyledButton = styled.button`
## Importe
Use aliases instead of relative paths:
Verwenden Sie Aliase statt relativer Pfade:
```tsx
// ❌ Bad
@@ -153,7 +153,7 @@ import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## Folder Structure
## Ordnerstruktur
```
front
@@ -171,6 +171,6 @@ front
└── ui/ # Reusable UI components (display, input, feedback, ...)
```
* Modules can import from other modules, but `ui/` should stay dependency-free
* Use `internal/` subfolders for module-private code
* Components under 300 lines, services under 500 lines
* Module können aus anderen Modulen importieren, aber `ui/` sollte abhängigkeitsfrei bleiben
* Verwenden Sie `internal/`-Unterordner für modulinternen Code
* Komponenten unter 300 Zeilen, Services unter 500 Zeilen
@@ -1,35 +1,35 @@
---
title: APIs
icon: plug
description: REST and GraphQL APIs generated from your workspace schema.
description: Vom Schema Ihres Arbeitsbereichs generierte REST- und GraphQL-APIs.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## Schema-per-tenant APIs
## Schema-pro-Mandant-APIs
There is no static API reference for Twenty. Each workspace has its own schema — when you add a custom object (say `Invoice`), it immediately gets REST and GraphQL endpoints identical to built-in objects like `Company` or `Person`. The API is generated from the schema, so endpoints use your object and field names directly — no opaque IDs.
Es gibt keine statische API-Referenz für Twenty. Jeder Arbeitsbereich hat sein eigenes Schema — wenn Sie ein benutzerdefiniertes Objekt hinzufügen (z. B. `Invoice`), erhält es sofort REST- und GraphQL-Endpunkte, die mit den integrierten Objekten wie `Company` oder `Person` identisch sind. Die API wird aus dem Schema generiert, daher verwenden die Endpunkte Ihre Objekt- und Feldnamen direkt — keine undurchsichtigen IDs.
Your workspace-specific API documentation is available under **Settings → API & Webhooks** after creating an API key. It includes an interactive playground where you can execute real calls against your data.
Ihre arbeitsbereichsspezifische API-Dokumentation ist nach dem Erstellen eines API-Schlüssels unter **Einstellungen → API & Webhooks** verfügbar. Sie umfasst einen interaktiven Playground, in dem Sie echte Aufrufe gegen Ihre Daten ausführen können.
## Two APIs
## Zwei APIs
**Core API** — `/rest/` and `/graphql/`
**Core-API** — `/rest/` und `/graphql/`
CRUD on records: People, Companies, Opportunities, your custom objects. Query, filter, traverse relations.
CRUD für Datensätze: Personen, Unternehmen, Verkaufschancen, Ihre benutzerdefinierten Objekte. Abfragen, filtern, Beziehungen durchlaufen.
**Metadata API** — `/rest/metadata/` and `/metadata/`
**Metadaten-API** — `/rest/metadata/` und `/metadata/`
Schema management: create/modify/delete objects, fields, and relations. This is how you programmatically change your data model.
Schemaverwaltung: Objekte, Felder und Beziehungen erstellen/ändern/löschen. So ändern Sie Ihr Datenmodell programmatisch.
Both are available as REST and GraphQL. GraphQL adds batch upserts and the ability to traverse relations in a single query. Same underlying data either way.
Beide sind als REST und GraphQL verfügbar. GraphQL bietet Batch-Upserts und die Möglichkeit, Beziehungen in einer einzigen Abfrage zu durchlaufen. Die zugrunde liegenden Daten sind in beiden Fällen gleich.
## Base URLs
## Basis-URLs
| Umgebung | Basis-URL |
| ----------- | ------------------------- |
| Cloud | `https://api.twenty.com/` |
| Self-Hosted | `https://{your-domain}/` |
| Umgebung | Basis-URL |
| ------------- | ------------------------- |
| Cloud | `https://api.twenty.com/` |
| Selbsthosting | `https://{your-domain}/` |
## Authentifizierung
@@ -37,19 +37,19 @@ Both are available as REST and GraphQL. GraphQL adds batch upserts and the abili
Authorization: Bearer YOUR_API_KEY
```
Create an API key in **Settings API & Webhooks + Create key**. Copy it immediately — it's shown once. Keys can be scoped to a specific role under **Settings Roles Assignment tab** to limit what they can access.
Erstellen Sie einen API-Schlüssel unter **Settings > APIs & Webhooks > + Create key**. Kopieren Sie ihn sofort — er wird nur einmal angezeigt. Schlüssel können unter **Settings > Roles > Assignment tab** auf eine bestimmte Rolle beschränkt werden, um ihren Zugriff einzuschränken.
<VimeoEmbed videoId="928786722" title="API-Schlüssel erstellen" />
For OAuth-based access (external apps acting on behalf of users), see [OAuth](/l/de/developers/extend/oauth).
Für OAuth-basierten Zugriff (externe Apps, die im Namen von Nutzern handeln), siehe [OAuth](/l/de/developers/extend/oauth).
## Batch operations
## Batch-Vorgänge
Both REST and GraphQL support batching up to 60 records per request — create, update, or delete. GraphQL also supports batch upsert (create-or-update in one call) using plural names like `CreateCompanies`.
Sowohl REST als auch GraphQL unterstützen Batching von bis zu 60 Datensätzen pro Anfrage — erstellen, aktualisieren oder löschen. GraphQL unterstützt außerdem Batch-Upsert (Erstellen-oder-Aktualisieren in einem Aufruf) mit Pluralnamen wie `CreateCompanies`.
## Rate limits
## Rate Limits
| Limit | Wert |
| ---------- | ------------------------ |
| Requests | 100 per minute |
| Batch size | 60 Datensätze pro Aufruf |
| Limit | Wert |
| ----------- | ------------------------ |
| Anfragen | 100 Aufrufe pro Minute |
| Batch-Größe | 60 Datensätze pro Aufruf |
@@ -1,14 +1,14 @@
---
title: Architektur
description: How Twenty apps worksandboxing, lifecycle, and the building blocks.
description: Wie Twenty-Apps funktionierenSandboxing, Lebenszyklus und Bausteine.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Twenty-Apps sind TypeScript-Pakete, die Ihren Arbeitsbereich mit benutzerdefinierten Objekten, Logik, UI-Komponenten und KI-Funktionen erweitern. Sie laufen auf der Twenty-Plattform mit vollständigem Sandboxing und Berechtigungsverwaltung.
## How apps work
## Wie Apps funktionieren
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Eine App ist eine Sammlung von **Entitäten**, die mithilfe von `defineEntity()`-Funktionen aus dem Paket `twenty-sdk` deklariert werden. Das SDK erkennt diese Deklarationen zur Build-Zeit per AST-Analyse und erzeugt ein **Manifest** — eine vollständige Beschreibung dessen, was Ihre App zu einem Arbeitsbereich hinzufügt.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**Die Dateiorganisation liegt bei Ihnen.** Die Entitätserkennung ist AST-basiert — das SDK findet Aufrufe von `export default defineEntity(...)`, unabhängig davon, wo sich die Datei befindet. Die obige Ordnerstruktur ist eine Konvention, keine Anforderung.
</Note>
## Entity types
## Entitätstypen
| Entität | Zweck | Dokumentation |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/de/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/de/developers/extend/apps/data-model) |
| **Object** | Custom data tables with fields | [Data Model](/l/de/developers/extend/apps/data-model) |
| **Feld** | Extend existing objects, define relations | [Data Model](/l/de/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Logikfunktionen](/l/de/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/de/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/de/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/de/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/de/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/de/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/de/developers/extend/apps/layout) |
| Entität | Zweck | Dokumentation |
| -------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- |
| **Anwendung** | App-Identität, Berechtigungen, Variablen | [Datenmodell](/l/de/developers/extend/apps/data-model) |
| **Rolle** | Berechtigungssätze für Objekte und Felder | [Datenmodell](/l/de/developers/extend/apps/data-model) |
| **Object** | Benutzerdefinierte Datentabellen mit Feldern | [Datenmodell](/l/de/developers/extend/apps/data-model) |
| **Feld** | Bestehende Objekte erweitern, Relationen definieren | [Datenmodell](/l/de/developers/extend/apps/data-model) |
| **Logikfunktion** | Serverseitiges TypeScript mit Triggern | [Logikfunktionen](/l/de/developers/extend/apps/logic-functions) |
| **Frontend-Komponente** | Sandboxed React-UI auf der Twenty-Seite | [Frontend-Komponenten](/l/de/developers/extend/apps/front-components) |
| **Skill** | Wiederverwendbare Anweisungen für KI-Agenten | [Skills & Agenten](/l/de/developers/extend/apps/skills-and-agents) |
| **Agent** | KI-Assistenten mit benutzerdefinierten Prompts | [Skills & Agenten](/l/de/developers/extend/apps/skills-and-agents) |
| **Ansicht** | Vorkonfigurierte Listenansichten für Datensätze | [Layout](/l/de/developers/extend/apps/layout) |
| **Navigationsmenüeintrag** | Benutzerdefinierte Seitenleisten-Einträge | [Layout](/l/de/developers/extend/apps/layout) |
| **Seitenlayout** | Benutzerdefinierte Registerkarten und Widgets auf Datensatzseiten | [Layout](/l/de/developers/extend/apps/layout) |
## Sandboxing
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Logikfunktionen** laufen in isolierten Node.js-Prozessen auf dem Server. Sie greifen nur über den typisierten API-Client auf Daten zu, begrenzt durch die Rollenberechtigungen der App.
* **Frontend-Komponenten** laufen in Web Workers mit Remote DOM — von der Hauptseite isoliert, rendern aber native DOM-Elemente (keine iframes). Sie kommunizieren über eine Message-Passing-Host-API mit Twenty.
* **Berechtigungen** werden auf API-Ebene durchgesetzt. Das Laufzeit-Token (`TWENTY_APP_ACCESS_TOKEN`) wird aus der in `defineApplication()` definierten Rolle abgeleitet.
## App lifecycle
## App-Lebenszyklus
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/de/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — überwacht Ihre Quelldateien und synchronisiert Änderungen in Echtzeit mit einem verbundenen Twenty-Server. Der typisierte API-Client wird automatisch neu erzeugt, wenn sich das Schema ändert.
* **`yarn twenty build`** — kompiliert TypeScript, bündelt Logikfunktionen und Frontend-Komponenten mit esbuild und erzeugt ein Manifest.
* **Pre/Post-Install-Hooks** — optionale Logikfunktionen, die während der Installation ausgeführt werden. Details finden Sie unter [Logikfunktionen](/l/de/developers/extend/apps/logic-functions).
## Nächste Schritte
<CardGroup cols={2}>
<Card title="Datenmodell" icon="database" href="/l/de/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Objekte, Felder, Rollen und Relationen definieren.
</Card>
<Card title="Logikfunktionen" icon="bolt" href="/l/de/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
Serverseitige Funktionen mit HTTP-, cron- und Ereignis-Triggern.
</Card>
<Card title="Frontend-Komponenten" icon="window-maximize" href="/l/de/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Sandboxed React-Komponenten innerhalb der UI von Twenty.
</Card>
<Card title="Layout" icon="table-columns" href="/l/de/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Ansichten, Navigationseinträge und Layouts von Datensatzseiten.
</Card>
<Card title="Fähigkeiten & Agenten" icon="robot" href="/l/de/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
KI-Skills und Agenten mit benutzerdefinierten Prompts.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/de/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI & Tests" icon="terminal" href="/l/de/developers/extend/apps/cli-and-testing">
CLI-Befehle, Tests, Assets, Remotes und CI.
</Card>
<Card title="Veröffentlichen" icon="rocket" href="/l/de/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Auf einem Server bereitstellen oder auf dem Marktplatz veröffentlichen.
</Card>
</CardGroup>
@@ -1,23 +1,23 @@
---
title: Layout
description: Define views, navigation menu items, and page layouts to shape how your app appears in Twenty.
description: Definieren Sie Ansichten, Navigationsmenüeinträge und Seitenlayouts, um das Erscheinungsbild Ihrer App in Twenty zu gestalten.
icon: table-columns
---
Layout entities control how your app surfaces inside Twenty's UI — what lives in the sidebar, which saved views ship with the app, and how a record detail page is arranged.
Layout-Entitäten steuern, wie Ihre App innerhalb der Benutzeroberfläche von Twenty dargestellt wird — was in der Seitenleiste angezeigt wird, welche gespeicherten Ansichten mit der App ausgeliefert werden und wie eine Detailseite eines Datensatzes angeordnet ist.
## Layout concepts
## Layout-Konzepte
| Concept | What it controls | Entität |
| ------------------------ | --------------------------------------------------------------------------------- | -------------------------- |
| **View** | A saved list configuration for an object — visible fields, order, filters, groups | `defineView` |
| **Navigation Menu Item** | An entry in the left sidebar that links to a view or an external URL | `defineNavigationMenuItem` |
| **Page Layout** | The tabs and widgets that make up a record's detail page | `definePageLayout` |
| Konzept | Was es steuert | Entität |
| -------------------------- | ------------------------------------------------------------------------------------------------------ | -------------------------- |
| **Ansicht** | Eine gespeicherte Listen-Konfiguration für ein Objekt — sichtbare Felder, Reihenfolge, Filter, Gruppen | `defineView` |
| **Navigationsmenüeintrag** | Ein Eintrag in der linken Seitenleiste, der auf eine Ansicht oder eine externe URL verweist | `defineNavigationMenuItem` |
| **Seitenlayout** | Die Tabs und Widgets, aus denen die Detailseite eines Datensatzes besteht | `definePageLayout` |
Views, navigation items, and page layouts reference each other by `universalIdentifier`:
Ansichten, Navigationsmenüeinträge und Seitenlayouts verweisen über `universalIdentifier` aufeinander:
* A **navigation menu item** of type `VIEW` points at a `defineView` identifier, so the sidebar link opens that saved view.
* A **page layout** of type `RECORD_PAGE` targets an object and can embed [front components](/l/de/developers/extend/apps/front-components) inside its tabs as widgets.
* Ein **Navigationsmenüeintrag** vom Typ `VIEW` verweist auf einen `defineView`-Bezeichner, sodass der Seitenleistenlink diese gespeicherte Ansicht öffnet.
* Ein **Seitenlayout** vom Typ `RECORD_PAGE` zielt auf ein Objekt ab und kann [Frontkomponenten](/l/de/developers/extend/apps/front-components) innerhalb seiner Tabs als Widgets einbetten.
<AccordionGroup>
<Accordion title="defineView" description="Gespeicherte Views für Objekte definieren">
@@ -1,6 +1,6 @@
---
title: Fähigkeiten & Agenten
description: Define AI skills and agents for your app.
description: Definieren Sie KI-Skills und Agenten für Ihre App.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. Die Funktion ist funktionsfähig, entwickelt sich jedoch noch weiter.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Apps können KI-Funktionen definieren, die im Arbeitsbereich verfügbar sind — wiederverwendbare Skill-Anweisungen und Agenten mit benutzerdefinierten System-Prompts.
<AccordionGroup>
<Accordion title="defineSkill" description="Skills für KI-Agenten definieren">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: schlüssel
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: Autorisierungscode-Flow mit PKCE und Client-Anmeldedaten für Server-zu-Server-Zugriff.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty implementiert OAuth 2.0 mit Autorisierungscode + PKCE für benutzerorientierte Apps und Client-Anmeldedaten für Server-zu-Server-Zugriff. Clients werden dynamisch über [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) registriert — keine manuelle Einrichtung in einem Dashboard.
## When to Use OAuth
## Wann Sie OAuth verwenden sollten
| Szenario | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/de/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/de/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Szenario | Authentifizierungsmethode |
| ------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Interne Skripte, Automatisierung | [API-Schlüssel](/l/de/developers/extend/api#authentication) |
| Externe App, die im Namen eines Benutzers handelt | **OAuth — Autorisierungscode** |
| Server-zu-Server, kein Benutzerkontext | **OAuth — Client-Anmeldedaten** |
| Twenty-App mit UI-Erweiterungen | [Apps](/l/de/developers/extend/apps/getting-started) (OAuth wird automatisch gehandhabt) |
## Register a Client
## Einen Client registrieren
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
Twenty unterstützt die **dynamische Client-Registrierung** gemäß [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). Keine manuelle Einrichtung erforderlich — registrieren Sie den Client programmgesteuert:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Antwort:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
Speichern Sie das `client_secret` sicher — es kann später nicht mehr abgerufen werden.
</Warning>
## Geltungsbereiche
| Scope | Zugriff |
| -------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `profil` | Read the authenticated user's profile information |
| Geltungsbereich | Zugriff |
| --------------- | ------------------------------------------------------------ |
| `api` | Voller Lese-/Schreibzugriff auf die Core- und Metadaten-APIs |
| `profile` | Profilinformationen des authentifizierten Benutzers lesen |
Request scopes as a space-separated string: `scope=api profile`
Fordern Sie Geltungsbereiche als durch Leerzeichen getrennte Zeichenfolge an: `scope=api profile`
## Authorization Code Flow
## Autorisierungscode-Flow
Use this flow when your app acts on behalf of a Twenty user.
Verwenden Sie diesen Flow, wenn Ihre App im Namen eines Twenty-Benutzers handelt.
### 1. Redirect the user to authorize
### 1. Leiten Sie den Benutzer zur Autorisierung weiter
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Parameter | Erforderlich | Beschreibung |
| ----------------------- | ------------ | ------------------------------------------------------------ |
| `client_id` | Ja | Your registered client ID |
| `response_type` | Ja | Must be `code` |
| `redirect_uri` | Ja | Must match a registered redirect URI |
| `scope` | Nein | Space-separated scopes (defaults to `api`) |
| `zustand` | Empfohlen | Random string to prevent CSRF attacks |
| `code_challenge` | Empfohlen | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Empfohlen | Must be `S256` when using PKCE |
| Parameter | Erforderlich | Beschreibung |
| ----------------------- | ------------ | -------------------------------------------------------------- |
| `client_id` | Ja | Ihre registrierte Client-ID |
| `response_type` | Ja | Muss `code` sein |
| `redirect_uri` | Ja | Muss einer registrierten Redirect-URI entsprechen |
| `scope` | Nein | Durch Leerzeichen getrennte Geltungsbereiche (Standard: `api`) |
| `state` | Empfohlen | Zufällige Zeichenfolge zur Verhinderung von CSRF-Angriffen |
| `code_challenge` | Empfohlen | PKCE-Challenge (SHA-256-Hash des Verifiers, base64url-codiert) |
| `code_challenge_method` | Empfohlen | Muss bei Verwendung von PKCE `S256` sein |
The user sees a consent screen and approves or denies access.
Der Benutzer sieht einen Zustimmungsbildschirm und stimmt dem Zugriff zu oder lehnt ihn ab.
### 2. Handle the callback
### 2. Den Callback verarbeiten
After authorization, Twenty redirects back to your `redirect_uri`:
Nach der Autorisierung leitet Twenty zurück zu Ihrer `redirect_uri` weiter:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
Überprüfen Sie, dass `state` mit dem übereinstimmt, was Sie gesendet haben.
### 3. Exchange the code for tokens
### 3. Tauschen Sie den Code gegen Token aus
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Antwort:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Verwenden Sie das Zugriffstoken
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Aktualisieren, wenn abgelaufen
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## Client-Anmeldedaten-Flow
For server-to-server integrations with no user interaction:
Für Server-zu-Server-Integrationen ohne Benutzerinteraktion:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
Das zurückgegebene Token hat Zugriff auf Arbeitsbereichsebene und ist an keinen bestimmten Benutzer gebunden.
## Server Discovery
## Server-Ermittlung
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty veröffentlicht seine OAuth-Konfiguration an einem standardisierten Discovery-Endpunkt:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Dies liefert alle Endpunkte, unterstützte Grant-Typen, Geltungsbereiche und Fähigkeiten — nützlich, um generische OAuth-Clients zu erstellen.
## API Endpoints Summary
## Zusammenfassung der API-Endpunkte
| Endpunkt | Zweck |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Endpunkt | Zweck |
| ----------------------------------------- | ----------------------------------- |
| `/.well-known/oauth-authorization-server` | Ermittlung von Servermetadaten |
| `/oauth/register` | Dynamische Client-Registrierung |
| `/oauth/authorize` | Benutzerautorisierung |
| `/oauth/token` | Token-Austausch und -Aktualisierung |
| Umgebung | Basis-URL |
| ----------------- | ------------------------ |
| **Cloud** | `https://api.twenty.com` |
| **Selbsthosting** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth vs. API-Schlüssel
| | API-Schlüssel | OAuth |
| -------------------------- | ----------------------- | -------------------------------------- |
| **Einrichtung** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Am besten geeignet für** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Manuell | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API-Schlüssel | OAuth |
| ------------------------------------- | ------------------------------- | ----------------------------------------- |
| **Einrichtung** | In den Einstellungen generieren | Client registrieren, Flow implementieren |
| **Benutzerkontext** | Keiner (Arbeitsbereichsebene) | Berechtigungen eines bestimmten Benutzers |
| **Am besten geeignet für** | Skripte, interne Tools | Externe Apps, Multi-User-Integrationen |
| **Token-Rotation** | Manuell | Automatisch über Refresh-Tokens |
| **Geltungsbereichsbasierter Zugriff** | Voller API-Zugriff | Granular über Geltungsbereiche |
@@ -1,12 +1,12 @@
---
title: Webhooks
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Lassen Sie sich benachrichtigen, wenn sich Datensätze ändern — HTTP POST an Ihren Endpunkt bei jeder Erstellung, Aktualisierung oder Löschung.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Twenty sendet einen HTTP POST an Ihre URL, wenn ein Datensatz erstellt, aktualisiert oder gelöscht wird. Alle Objekttypen sind abgedeckt, einschließlich benutzerdefinierter Objekte.
## Webhook erstellen
@@ -1,6 +1,6 @@
---
title: Fehlerbehebung
icon: wrench
icon: Schraubenschlüssel
---
## Fehlerbehebung
@@ -1,6 +1,6 @@
---
title: App-Tooltip
icon: nachricht
icon: Nachricht
---
<Frame>
@@ -38,15 +38,15 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------ |
| linkToEntity | Zeichenkette | Der Link zur Entität |
| entityId | Zeichenkette | Der eindeutige Identifikator für die Entität |
| name | string | Der Name der Entität |
| pictureUrl | Zeichenkette | s Bild", |
| avatarType | Avatar-Typ | Der Typ des Avatars, den Sie anzeigen möchten. Hat zwei Optionen: `abgerundet` und `quadratisch` |
| Variante | `EntityChipVariante` enum | Variante des Entity-Chips, die Sie anzeigen möchten. Hat zwei Optionen: `regelmäßig` und `transparent` |
| LeftIcon | Icon-Komponente | Eine React-Komponente, die ein Symbol darstellt. Wird auf der linken Seite des Chips angezeigt |
| Props | Typ | Beschreibung |
| ------------ | ------------------------- | ------------------------------------------------------------------------------------------------------ |
| linkToEntity | Zeichenkette | Der Link zur Entität |
| entityId | Zeichenkette | Der eindeutige Identifikator für die Entität |
| name | string | Der Name der Entität |
| pictureUrl | Zeichenkette | s Bild", |
| avatarType | Avatar-Typ | Der Typ des Avatars, den Sie anzeigen möchten. Hat zwei Optionen: `abgerundet` und `quadratisch` |
| Variante | `EntityChipVariante` enum | Variante des Entity-Chips, die Sie anzeigen möchten. Hat zwei Optionen: `regelmäßig` und `transparent` |
| LeftIcon | Icon-Komponente | Eine React-Komponente, die ein Symbol darstellt. Wird auf der linken Seite des Chips angezeigt |
@@ -137,15 +137,15 @@ export const MyComponent = () => {
<Tab title="Eigenschaften">
| Eigenschaften | Typ | Beschreibung |
| ------------- | ------------------------- | ------------------------------------------------------------------------------------------------------ |
| linkToEntity | string | Der Link zur Entität |
| entityId | string | Der eindeutige Identifikator für die Entität |
| name | string | Der Name der Entität |
| pictureUrl | Zeichenfolge | s Bild", |
| avatarType | Avatar-Typ | Der Typ des Avatars, den Sie anzeigen möchten. Hat zwei Optionen: `abgerundet` und `quadratisch` |
| Variante | `EntityChipVariante` enum | Variante des Entity-Chips, die Sie anzeigen möchten. Hat zwei Optionen: `regelmäßig` und `transparent` |
| LeftIcon | Icon-Komponente | Eine React-Komponente, die ein Symbol darstellt. Wird auf der linken Seite des Chips angezeigt |
| Props | Typ | Beschreibung |
| ------------ | ------------------------- | ------------------------------------------------------------------------------------------------------ |
| linkToEntity | string | Der Link zur Entität |
| entityId | string | Der eindeutige Identifikator für die Entität |
| name | string | Der Name der Entität |
| pictureUrl | Zeichenfolge | s Bild", |
| avatarType | Avatar-Typ | Der Typ des Avatars, den Sie anzeigen möchten. Hat zwei Optionen: `abgerundet` und `quadratisch` |
| Variante | `EntityChipVariante` enum | Variante des Entity-Chips, die Sie anzeigen möchten. Hat zwei Optionen: `regelmäßig` und `transparent` |
| LeftIcon | Icon-Komponente | Eine React-Komponente, die ein Symbol darstellt. Wird auf der linken Seite des Chips angezeigt |
@@ -1,6 +1,6 @@
---
title: '"Tag"'
icon: '"tag"'
icon: tag
---
Komponente zur visuellen Kategorisierung oder Kennzeichnung von Inhalten.
@@ -26,9 +26,9 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ----------------- | ------------------------------------------- |
| Editor | `BlockNoteEditor` | Instanz oder Konfiguration des Blockeditors |
| Props | Typ | Beschreibung |
| ------ | ----------------- | ------------------------------------------- |
| Editor | `BlockNoteEditor` | Instanz oder Konfiguration des Blockeditors |
@@ -40,7 +40,7 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| Props | Typ | Beschreibung |
| --------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| deaktiviert | boolesch | Deaktiviert die Symbolauswahl, wenn auf `true` gesetzt ist. |
| beiÄnderung | function | Die Rückruffunktion wird ausgelöst, wenn der Benutzer ein Symbol auswählt. Es erhält ein Objekt mit `iconKey` und `Icon` Eigenschaften |
@@ -23,15 +23,15 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| bild | Zeichenkette | Die Bildquellen-URL |
| onUpload | Funktion | Die Funktion, die aufgerufen wird, wenn ein Benutzer ein neues Bild hochlädt. Es erhält das `Datei` Objekt als Parameter |
| onRemove | Funktion | Die Funktion wird aufgerufen, wenn der Benutzer auf die Entfernen-Schaltfläche klickt. |
| onAbort | Funktion | Die Funktion wird aufgerufen, wenn der Benutzer während des Bilduploads auf die Abbrechen-Schaltfläche klickt. |
| isUploading | boolesch | Gibt an, ob ein Bild derzeit hochgeladen wird |
| Fehlermeldung | Zeichenkette | Eine optionale Fehlermeldung, die unterhalb des Bildeingangs angezeigt wird. |
| deaktiviert | boolesch | Wenn `true`, ist die gesamte Eingabe deaktiviert und die Schaltflächen sind nicht anklickbar |
| Props | Typ | Beschreibung |
| ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| bild | Zeichenkette | Die Bildquellen-URL |
| onUpload | Funktion | Die Funktion, die aufgerufen wird, wenn ein Benutzer ein neues Bild hochlädt. Es erhält das `Datei` Objekt als Parameter |
| onRemove | Funktion | Die Funktion wird aufgerufen, wenn der Benutzer auf die Entfernen-Schaltfläche klickt. |
| onAbort | Funktion | Die Funktion wird aufgerufen, wenn der Benutzer während des Bilduploads auf die Abbrechen-Schaltfläche klickt. |
| isUploading | boolesch | Gibt an, ob ein Bild derzeit hochgeladen wird |
| Fehlermeldung | Zeichenkette | Eine optionale Fehlermeldung, die unterhalb des Bildeingangs angezeigt wird. |
| deaktiviert | boolesch | Wenn `true`, ist die gesamte Eingabe deaktiviert und die Schaltflächen sind nicht anklickbar |
@@ -38,14 +38,14 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Klassenname | Zeichenkette | Optionale CSS-Klasse für zusätzliche Stilgebung |
| deaktiviert | boolesch | Wenn auf `true` gesetzt, wird die Benutzerinteraktion mit der Komponente deaktiviert |
| Beschriftung | string | Die Beschriftung, um den Zweck der `Select`-Komponente zu beschreiben |
| onChange | Funktion | Die Funktion, die aufgerufen wird, wenn sich die ausgewählten Werte ändern |
| optionen | Array | Repräsentiert die verfügbaren Optionen für die `Select`-Komponente. Es ist ein Array von Objekten, bei dem jedes Objekt ein `value` (die eindeutige Kennung), `label` (die eindeutige Kennung) und ein optionales `Icon` hat |
| wert | Zeichenkette | Repräsentiert den aktuell ausgewählten Wert. Es sollte mit einem der `value`-Eigenschaften im `options`-Array übereinstimmen |
| Props | Typ | Beschreibung |
| ------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Klassenname | Zeichenkette | Optionale CSS-Klasse für zusätzliche Stilgebung |
| deaktiviert | boolesch | Wenn auf `true` gesetzt, wird die Benutzerinteraktion mit der Komponente deaktiviert |
| Beschriftung | string | Die Beschriftung, um den Zweck der `Select`-Komponente zu beschreiben |
| onChange | Funktion | Die Funktion, die aufgerufen wird, wenn sich die ausgewählten Werte ändern |
| optionen | Array | Repräsentiert die verfügbaren Optionen für die `Select`-Komponente. Es ist ein Array von Objekten, bei dem jedes Objekt ein `value` (die eindeutige Kennung), `label` (die eindeutige Kennung) und ein optionales `Icon` hat |
| wert | Zeichenkette | Repräsentiert den aktuell ausgewählten Wert. Es sollte mit einem der `value`-Eigenschaften im `options`-Array übereinstimmen |
@@ -48,7 +48,7 @@ export const MyComponent = () => {
<Tab title="Eigenschaften">
| Eigenschaften | Typ | Beschreibung |
| Props | Typ | Beschreibung |
| -------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| className | Zeichenkette | Optionaler Name für zusätzliche Stile |
| Beschriftung | Zeichenkette | Stellt die Beschriftung für das Eingabefeld dar |
@@ -100,15 +100,15 @@ export const MyComponent = () => {
<Tab title="Eigenschaften">
| Eigenschaften | Typ | Beschreibung |
| ------------- | ------------ | ---------------------------------------------------------------------------------------- |
| onValidate | Funktion | Die Callback-Funktion, die Sie auslösen möchten, wenn der Benutzer die Eingabe validiert |
| minRows | nummer | Die minimale Anzahl von Zeilen für den Textbereich |
| Platzhalter | Zeichenkette | Der Platzhaltertext, den Sie anzeigen möchten, wenn der Textbereich leer ist |
| onFocus | Funktion | Die Callback-Funktion, die Sie auslösen möchten, wenn der Textbereich den Fokus erlangt |
| Variante | Zeichenkette | Die Variante der Eingabe. Optionen umfassen: `Standard`, `Ikone` und `Schaltfläche` |
| buttonTitle | Zeichenkette | Der Titel für die Schaltfläche (nur für die Schaltflächenvariante anwendbar) |
| wert | Zeichenkette | Der Initialwert für den Textbereich |
| Props | Typ | Beschreibung |
| ----------- | ------------ | ---------------------------------------------------------------------------------------- |
| onValidate | Funktion | Die Callback-Funktion, die Sie auslösen möchten, wenn der Benutzer die Eingabe validiert |
| minRows | nummer | Die minimale Anzahl von Zeilen für den Textbereich |
| Platzhalter | Zeichenkette | Der Platzhaltertext, den Sie anzeigen möchten, wenn der Textbereich leer ist |
| onFocus | Funktion | Die Callback-Funktion, die Sie auslösen möchten, wenn der Textbereich den Fokus erlangt |
| Variante | Zeichenkette | Die Variante der Eingabe. Optionen umfassen: `Standard`, `Ikone` und `Schaltfläche` |
| buttonTitle | Zeichenkette | Der Titel für die Schaltfläche (nur für die Schaltflächenvariante anwendbar) |
| wert | Zeichenkette | Der Initialwert für den Textbereich |
@@ -146,13 +146,13 @@ export const MyComponent = () => {
<Tab title="Eigenschaften">
| Eigenschaften | Typ | Beschreibung |
| ------------- | ------------ | ---------------------------------------------------------------------------- |
| deaktiviert | boolesch | Gibt an, ob der Textbereich deaktiviert ist |
| minRows | nummer | Minimale Anzahl sichtbarer Zeilen für den Textbereich. |
| onChange | Funktion | Rückruffunktion wird ausgelöst, wenn sich der Inhalt des Textbereichs ändert |
| Platzhalter | Zeichenkette | Platzhaltertext, der angezeigt wird, wenn der Textbereich leer ist |
| wert | Zeichenkette | Der aktuelle Wert des Textbereichs |
| Props | Typ | Beschreibung |
| ----------- | ------------ | ---------------------------------------------------------------------------- |
| deaktiviert | boolesch | Gibt an, ob der Textbereich deaktiviert ist |
| minRows | nummer | Minimale Anzahl sichtbarer Zeilen für den Textbereich. |
| onChange | Funktion | Rückruffunktion wird ausgelöst, wenn sich der Inhalt des Textbereichs ändert |
| Platzhalter | Zeichenkette | Platzhaltertext, der angezeigt wird, wenn der Textbereich leer ist |
| wert | Zeichenkette | Der aktuelle Wert des Textbereichs |
@@ -35,10 +35,10 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Klassenname | Zeichenkette | Optionaler Klassenname für zusätzliche Stilierung |
| Links | array | Ein Array von Objekten, die jeweils einen Breadcrumb-Link darstellen. Jedes Objekt hat eine `children`-Eigenschaft (den textlichen Inhalt des Links) und eine optionale `href`-Eigenschaft (die URL, zu der navigiert wird, wenn der Link angeklickt wird) |
| Props | Typ | Beschreibung |
| ----------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Klassenname | Zeichenkette | Optionaler Klassenname für zusätzliche Stilierung |
| Links | array | Ein Array von Objekten, die jeweils einen Breadcrumb-Link darstellen. Jedes Objekt hat eine `children`-Eigenschaft (den textlichen Inhalt des Links) und eine optionale `href`-Eigenschaft (die URL, zu der navigiert wird, wenn der Link angeklickt wird) |
@@ -30,9 +30,9 @@ export const MyComponent = () => {
<Tab title="&#x22;Eigenschaften&#x22;">
| "Eigenschaften" | Typ | Beschreibung |
| --------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| aktiverSchritt | nummer | Der Index des derzeit aktiven Schritts. Dies bestimmt, welcher Schritt visuell hervorgehoben werden soll. |
| Props | Typ | Beschreibung |
| -------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| aktiverSchritt | nummer | Der Index des derzeit aktiven Schritts. Dies bestimmt, welcher Schritt visuell hervorgehoben werden soll. |
@@ -3,7 +3,7 @@ title: Navigation
description: Passen Sie die linke Seitenleiste an die Arbeitsweise Ihres Teams an.
---
Die linke Seitenleiste ist Ihre wichtigste Möglichkeit, sich in Twenty zu bewegen. Sie ist vollständig anpassbar — Sie können sie neu organisieren, damit sie zu Ihrem Workflow passt, ohne eine Einstellungsseite zu öffnen.
Die linke Seitenleiste ist Ihre wichtigste Möglichkeit, in Twenty zu navigieren. Sie ist vollständig anpassbar — Sie können sie neu organisieren, damit sie zu Ihrem Workflow passt, ohne eine Einstellungsseite zu öffnen.
## Einträge neu anordnen
@@ -21,7 +21,7 @@ Objekte, die Sie nicht verwenden, können aus der Seitenleiste ausgeblendet werd
## Favoriten
Heften Sie Ansichten, Datensätze oder Suchvorgänge im Bereich Favoriten oben in der Seitenleiste an, um mit einem Klick darauf zuzugreifen. Favoriten sind persönlich — jeder Nutzer verwaltet seine eigenen.
Heften Sie Ansichten, Datensätze oder Suchvorgänge im Bereich Favoriten oben in der Seitenleiste an, um mit einem Klick darauf zuzugreifen. Favoriten sind persönlich — jeder Benutzer verwaltet seine eigenen.
## Benutzerdefinierte Links
@@ -1,6 +1,6 @@
---
title: Comandi Backend
icon: terminal
icon: terminale
---
## Comandi utili
@@ -1,12 +1,12 @@
---
title: Commands
icon: terminal
description: Useful commands for developing Twenty.
title: Comandi
icon: terminale
description: Comandi utili per sviluppare Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
I comandi possono essere eseguiti dalla radice del repository usando `npx nx`. Usa `npx nx run {project}:{command}` per specificare esplicitamente il target.
## Starting the App
## Avviare l'app
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Controllo dei tipi
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## Compilazione
```bash
npx nx build twenty-shared # Must be built first
@@ -140,7 +140,7 @@ const StyledButton = styled.button`
`;
```
## Importa
## Importazioni
Use aliases instead of relative paths:
@@ -1,14 +1,14 @@
---
title: Architettura
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Come funzionano le app di Twenty — sandboxing, ciclo di vita e componenti di base.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Le app di Twenty sono pacchetti TypeScript che estendono il tuo spazio di lavoro con oggetti personalizzati, logica, componenti dell'interfaccia utente (UI) e funzionalità di IA. Vengono eseguite sulla piattaforma Twenty con sandboxing completo e controlli delle autorizzazioni.
## How apps work
## Come funzionano le app
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Un'app è una raccolta di **entità** dichiarate utilizzando le funzioni `defineEntity()` del pacchetto `twenty-sdk`. L'SDK rileva queste dichiarazioni tramite analisi dell'AST in fase di build e produce un **manifest** — una descrizione completa di ciò che la tua app aggiunge a uno spazio di lavoro.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**L'organizzazione dei file dipende da te.** Il rilevamento delle entità è basato sull'AST — l'SDK trova le chiamate a `export default defineEntity(...)` indipendentemente da dove si trova il file. La struttura delle cartelle sopra è una convenzione, non un requisito.
</Note>
## Entity types
## Tipi di entità
| Entità | Scopo | Documentazione |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/it/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/it/developers/extend/apps/data-model) |
| **Oggetto** | Custom data tables with fields | [Data Model](/l/it/developers/extend/apps/data-model) |
| **Campo** | Extend existing objects, define relations | [Data Model](/l/it/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Funzioni logiche](/l/it/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/it/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/it/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/it/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/it/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/it/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/it/developers/extend/apps/layout) |
| Entità | Scopo | Documentazione |
| -------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| **Applicazione** | Identità dell'app, autorizzazioni, variabili | [Modello dati](/l/it/developers/extend/apps/data-model) |
| **Ruolo** | Set di autorizzazioni per oggetti e campi | [Modello dati](/l/it/developers/extend/apps/data-model) |
| **Oggetto** | Tabelle di dati personalizzate con campi | [Modello dati](/l/it/developers/extend/apps/data-model) |
| **Campo** | Estendi gli oggetti esistenti, definisci le relazioni | [Modello dati](/l/it/developers/extend/apps/data-model) |
| **Funzione logica** | TypeScript lato server con trigger | [Funzioni logiche](/l/it/developers/extend/apps/logic-functions) |
| **Componente front-end** | UI React in sandbox nella pagina di Twenty | [Componenti front-end](/l/it/developers/extend/apps/front-components) |
| **Abilità** | Istruzioni riutilizzabili per agenti IA | [Abilità e agenti](/l/it/developers/extend/apps/skills-and-agents) |
| **Agente** | Assistenti IA con prompt personalizzati | [Abilità e agenti](/l/it/developers/extend/apps/skills-and-agents) |
| **Vista** | Viste di elenco dei record preconfigurate | [Layout](/l/it/developers/extend/apps/layout) |
| **Voce del menu di navigazione** | Voci della barra laterale personalizzate | [Layout](/l/it/developers/extend/apps/layout) |
| **Layout di pagina** | Schede e widget personalizzati nelle pagine dei record | [Layout](/l/it/developers/extend/apps/layout) |
## Sandboxing
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Le funzioni logiche** vengono eseguite in processi Node.js isolati sul server. Accedono ai dati solo tramite il client API tipizzato, con ambito limitato alle autorizzazioni del ruolo dell'app.
* **I componenti front-end** vengono eseguiti in Web Workers utilizzando il Remote DOM — isolati dalla pagina principale ma renderizzando elementi DOM nativi (non iframe). Comunicano con Twenty tramite un'API host basata sul passaggio di messaggi.
* **Le autorizzazioni** vengono applicate a livello di API. Il token di runtime (`TWENTY_APP_ACCESS_TOKEN`) è derivato dal ruolo definito in `defineApplication()`.
## App lifecycle
## Ciclo di vita dell'app
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/it/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — monitora i file sorgente e sincronizza in tempo reale le modifiche su un server Twenty connesso. Il client API tipizzato viene rigenerato automaticamente quando lo schema cambia.
* **`yarn twenty build`** — compila TypeScript, crea i bundle delle funzioni logiche e dei componenti front-end con esbuild e produce un manifest.
* **Hook di pre/post-installazione** — funzioni logiche opzionali che vengono eseguite durante l'installazione. Consulta [Funzioni logiche](/l/it/developers/extend/apps/logic-functions) per i dettagli.
## Prossimi passaggi
<CardGroup cols={2}>
<Card title="Modello dati" icon="database" href="/l/it/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Definisci oggetti, campi, ruoli e relazioni.
</Card>
<Card title="Funzioni logiche" icon="bolt" href="/l/it/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
Funzioni lato server con trigger HTTP, cron ed eventi.
</Card>
<Card title="Componenti front-end" icon="window-maximize" href="/l/it/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Componenti React in sandbox nell'UI di Twenty.
</Card>
<Card title="Disposizione" icon="table-columns" href="/l/it/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Viste, voci di navigazione e layout delle pagine dei record.
</Card>
<Card title="Skill e agenti" icon="robot" href="/l/it/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
Abilità e agenti IA con prompt personalizzati.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/it/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI e test" icon="terminal" href="/l/it/developers/extend/apps/cli-and-testing">
Comandi CLI, test, asset, remoti e CI.
</Card>
<Card title="Pubblicazione" icon="rocket" href="/l/it/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Distribuisci su un server o pubblica sul marketplace.
</Card>
</CardGroup>
@@ -1,5 +1,5 @@
---
title: Disposizione
title: Layout
description: Define views, navigation menu items, and page layouts to shape how your app appears in Twenty.
icon: table-columns
---
@@ -1,6 +1,6 @@
---
title: Skill e agenti
description: Define AI skills and agents for your app.
description: Definisci skill e agenti di IA per la tua app.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. La funzionalità funziona ma è ancora in evoluzione.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Le app possono definire capacità di IA che risiedono all'interno dello spazio di lavoro — istruzioni di skill riutilizzabili e agenti con prompt di sistema personalizzati.
<AccordionGroup>
<Accordion title="defineSkill" description="Definisci le skill degli agenti IA">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: chiave
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: Flusso del codice di autorizzazione con PKCE e credenziali client per l'accesso da server a server.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty implementa OAuth 2.0 con codice di autorizzazione + PKCE per le app rivolte agli utenti e credenziali client per l'accesso da server a server. I client vengono registrati dinamicamente tramite [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — nessuna configurazione manuale in una dashboard.
## When to Use OAuth
## Quando utilizzare OAuth
| Scenario | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/it/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/it/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Scenario | Metodo di autenticazione |
| ------------------------------------------------- | -------------------------------------------------------------------------------- |
| Script interni, automazioni | [Chiave API](/l/it/developers/extend/api#authentication) |
| App esterna che agisce per conto di un utente | **OAuth — Codice di autorizzazione** |
| Da server a server, nessun contesto utente | **OAuth — Credenziali client** |
| App Twenty con estensioni dell'interfaccia utente | [App](/l/it/developers/extend/apps/getting-started) (OAuth è gestito automaticamente) |
## Register a Client
## Registrare un client
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
Twenty supporta la **registrazione dinamica dei client** secondo [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). Non è necessaria alcuna configurazione manuale — registra a livello di codice:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Risposta:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
Conserva `client_secret` in modo sicuro — non potrà essere recuperato in seguito.
</Warning>
## Ambiti
| Scope | Accesso |
| --------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `profilo` | Read the authenticated user's profile information |
| Ambito | Accesso |
| --------- | -------------------------------------------------------------- |
| `api` | Accesso completo in lettura/scrittura alle API Core e Metadata |
| `profilo` | Legge le informazioni del profilo dell'utente autenticato |
Request scopes as a space-separated string: `scope=api profile`
Richiedi gli ambiti come stringa separata da spazi: `scope=api profile`
## Authorization Code Flow
## Flusso con codice di autorizzazione
Use this flow when your app acts on behalf of a Twenty user.
Usa questo flusso quando la tua app agisce per conto di un utente Twenty.
### 1. Redirect the user to authorize
### 1. Reindirizza l'utente per autorizzare
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Parametro | Obbligatorio | Descrizione |
| ----------------------- | ------------ | ------------------------------------------------------------ |
| `client_id` | Sì | Your registered client ID |
| `response_type` | Sì | Must be `code` |
| `redirect_uri` | Sì | Must match a registered redirect URI |
| `scope` | No | Space-separated scopes (defaults to `api`) |
| `stato` | Consigliato | Random string to prevent CSRF attacks |
| `code_challenge` | Consigliato | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Consigliato | Must be `S256` when using PKCE |
| Parametro | Obbligatorio | Descrizione |
| ----------------------- | ------------ | ------------------------------------------------------------------- |
| `client_id` | Sì | ID client registrato |
| `response_type` | Sì | Deve essere `code` |
| `redirect_uri` | Sì | Deve corrispondere a un URI di reindirizzamento registrato |
| `scope` | No | Ambiti separati da spazi (predefinito `api`) |
| `stato` | Consigliato | Stringa casuale per prevenire attacchi CSRF |
| `code_challenge` | Consigliato | Challenge PKCE (hash SHA-256 del verifier, codificato in base64url) |
| `code_challenge_method` | Consigliato | Deve essere `S256` quando si usa PKCE |
The user sees a consent screen and approves or denies access.
L'utente vede una schermata di consenso e approva o nega l'accesso.
### 2. Handle the callback
### 2. Gestisci la callback
After authorization, Twenty redirects back to your `redirect_uri`:
Dopo l'autorizzazione, Twenty reindirizza al tuo `redirect_uri`:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
Verifica che `state` corrisponda a quanto inviato.
### 3. Exchange the code for tokens
### 3. Scambia il codice con i token
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Risposta:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Usa il token di accesso
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Aggiorna alla scadenza
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## Flusso delle credenziali del client
For server-to-server integrations with no user interaction:
Per integrazioni da server a server senza interazione dell'utente:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
Il token restituito ha accesso a livello di spazio di lavoro, non legato a un utente specifico.
## Server Discovery
## Scoperta del server
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty pubblica la propria configurazione OAuth in un endpoint di discovery standard:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Questo restituisce tutti gli endpoint, i tipi di grant supportati, gli ambiti e le funzionalità — utile per creare client OAuth generici.
## API Endpoints Summary
## Riepilogo degli endpoint API
| Endpoint | Scopo |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Endpoint | Scopo |
| ----------------------------------------- | -------------------------------------- |
| `/.well-known/oauth-authorization-server` | Individuazione dei metadati del server |
| `/oauth/register` | Registrazione dinamica dei client |
| `/oauth/authorize` | Autorizzazione utente |
| `/oauth/token` | Scambio e aggiornamento dei token |
| Ambiente | URL di base |
| ----------------- | ------------------------ |
| **Cloud** | `https://api.twenty.com` |
| **Auto-ospitato** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth vs Chiavi API
| | API Keys | OAuth |
| ------------------ | ----------------------- | -------------------------------------- |
| **Impostazione** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Ideale per** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Manuale | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API Keys | OAuth |
| ----------------------- | --------------------------------------- | ---------------------------------------- |
| **Impostazione** | Genera nelle Impostazioni | Registra un client, implementa il flusso |
| **Contesto utente** | Nessuno (a livello di spazio di lavoro) | Autorizzazioni dell'utente specifico |
| **Ideale per** | Script, strumenti interni | App esterne, integrazioni multiutente |
| **Rotazione dei token** | Manuale | Automatica tramite token di refresh |
| **Accesso con ambiti** | Accesso completo alle API | Granulare tramite ambiti |
@@ -1,12 +1,12 @@
---
title: Webhooks
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Ricevi una notifica quando i record cambiano — HTTP POST al tuo endpoint a ogni creazione, aggiornamento o eliminazione.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Twenty invia un HTTP POST al tuo URL ogni volta che un record viene creato, aggiornato o eliminato. Tutti i tipi di oggetto sono supportati, inclusi gli oggetti personalizzati.
## Crea un Webhook
@@ -26,9 +26,9 @@ export const MyComponent = () => {
<Tab title="Props">
| Props | Tipo | Descrizione |
| ------ | ----------------- | ---------------------------------------------------- |
| editor | `BlockNoteEditor` | L'istanza o la configurazione dell'editor di blocchi |
| Proprietà | Tipo | Descrizione |
| --------- | ----------------- | ---------------------------------------------------- |
| editor | `BlockNoteEditor` | L'istanza o la configurazione dell'editor di blocchi |
@@ -40,7 +40,7 @@ export const MyComponent = () => {
<Tab title="Props">
| Props | Tipo | Descrizione |
| Proprietà | Tipo | Descrizione |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| disabilitato | booleano | Disabilita il selettore di icone se impostato su `true` |
| onChange | funzione | La funzione di callback attivata quando l'utente seleziona un'icona. Riceve un oggetto con le proprietà `iconKey` e `Icon` |
@@ -48,7 +48,7 @@ export const MyComponent = () => {
<Tab title="Proprietà">
| Proprietà | Tipo | Descrizione |
| Props | Tipo | Descrizione |
| -------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| nomeClasse | stringa | Nome opzionale per lo stile aggiuntivo |
| etichetta | stringa | Rappresenta l'etichetta per l'input |
@@ -100,7 +100,7 @@ export const MyComponent = () => {
<Tab title="Proprietà">
| Proprietà | Tipo | Descrizione |
| Props | Tipo | Descrizione |
| -------------- | -------- | ------------------------------------------------------------------------------------- |
| suValida | funzione | La funzione di callback che si vuole attivare quando l'utente valida l'input |
| righeMinime | numero | Il numero minimo di righe per l'area di testo |
@@ -146,7 +146,7 @@ export const MyComponent = () => {
<Tab title="Proprietà">
| Proprietà | Tipo | Descrizione |
| Props | Tipo | Descrizione |
| ------------- | -------- | --------------------------------------------------------------------------- |
| disabilitato | booleano | Indica se l'area di testo è disabilitata |
| righeMinime | numero | Numero minimo di righe visibili per l'area di testo. |
@@ -1,12 +1,12 @@
---
title: Commands
title: Comandos
icon: terminal
description: Useful commands for developing Twenty.
description: Comandos úteis para desenvolver o Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
Os comandos podem ser executados a partir da raiz do repositório usando `npx nx`. Use `npx nx run {project}:{command}` para direcionar explicitamente.
## Starting the App
## Iniciando o aplicativo
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## Banco de Dados
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Verificação de tipos
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## Compilação
```bash
npx nx build twenty-shared # Must be built first
@@ -110,7 +110,7 @@ const value = process.env.MY_VALUE ?? 'default';
onClick?.();
```
## Nomeação
## Nomenclatura
* **Variables**: camelCase, descriptive (`email` not `value`, `fieldMetadata` not `fm`)
* **Constants**: SCREAMING_SNAKE_CASE
@@ -1,14 +1,14 @@
---
title: Arquitetura
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Como as aplicações Twenty funcionam — sandboxing, ciclo de vida e os blocos de construção.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
As aplicações Twenty são pacotes TypeScript que estendem seu espaço de trabalho com objetos personalizados, lógica, componentes de UI e recursos de IA. Elas são executadas na plataforma Twenty com sandboxing completo e controles de permissão.
## How apps work
## Como as aplicações funcionam
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Uma aplicação é uma coleção de **entidades** declaradas usando funções `defineEntity()` do pacote `twenty-sdk`. O SDK detecta essas declarações via análise de AST no momento da compilação e produz um **manifesto** — uma descrição completa do que seu aplicativo adiciona a um espaço de trabalho.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**A organização de arquivos fica a seu critério.** A detecção de entidades é baseada em AST — o SDK encontra chamadas a `export default defineEntity(...)` independentemente de onde o arquivo esteja. A estrutura de pastas acima é uma convenção, não um requisito.
</Note>
## Entity types
## Tipos de entidade
| Entidade | Finalidade | Documentação |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/pt/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/pt/developers/extend/apps/data-model) |
| **Objeto** | Custom data tables with fields | [Data Model](/l/pt/developers/extend/apps/data-model) |
| **Campo** | Extend existing objects, define relations | [Data Model](/l/pt/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Funções lógicas](/l/pt/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/pt/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/pt/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/pt/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/pt/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/pt/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/pt/developers/extend/apps/layout) |
| Entidade | Finalidade | Documentação |
| ----------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------- |
| **Aplicação** | Identidade da aplicação, permissões, variáveis | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) |
| **Papel** | Conjuntos de permissões para objetos e campos | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) |
| **Objeto** | Tabelas de dados personalizadas com campos | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) |
| **Campo** | Estender objetos existentes, definir relações | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) |
| **Função lógica** | TypeScript no lado do servidor com gatilhos | [Funções lógicas](/l/pt/developers/extend/apps/logic-functions) |
| **Componente de front-end** | UI React em sandbox na página do Twenty | [Componentes de front-end](/l/pt/developers/extend/apps/front-components) |
| **Habilidade** | Instruções reutilizáveis para agentes de IA | [Habilidades e Agentes](/l/pt/developers/extend/apps/skills-and-agents) |
| **Agente** | Assistentes de IA com prompts personalizados | [Habilidades e Agentes](/l/pt/developers/extend/apps/skills-and-agents) |
| **Vista** | Vistas de lista de registros pré-configuradas | [Layout](/l/pt/developers/extend/apps/layout) |
| **Item do menu de navegação** | Entradas personalizadas na barra lateral | [Layout](/l/pt/developers/extend/apps/layout) |
| **Layout da Página** | Abas e widgets personalizados nas páginas de registro | [Layout](/l/pt/developers/extend/apps/layout) |
## Sandboxing
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Funções lógicas** são executadas em processos Node.js isolados no servidor. Elas acessam dados apenas por meio do cliente de API tipado, restrito às permissões do papel do aplicativo.
* **Componentes de front-end** executam em Web Workers usando Remote DOM — isolados da página principal, mas renderizando elementos DOM nativos (não iframes). Eles se comunicam com o Twenty por meio de uma API de host com passagem de mensagens.
* **Permissões** são aplicadas no nível da API. O token de tempo de execução (`TWENTY_APP_ACCESS_TOKEN`) é derivado do papel definido em `defineApplication()`.
## App lifecycle
## Ciclo de vida do aplicativo
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/pt/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — observa seus arquivos-fonte e sincroniza ao vivo as alterações com um servidor Twenty conectado. O cliente de API tipado é regenerado automaticamente quando o esquema muda.
* **`yarn twenty build`** — compila TypeScript, empacota funções de lógica e componentes de front-end com o esbuild e produz um manifesto.
* **Hooks de pré/pós-instalação** — funções de lógica opcionais que são executadas durante a instalação. Veja [Funções de Lógica](/l/pt/developers/extend/apps/logic-functions) para detalhes.
## Próximos passos
<CardGroup cols={2}>
<Card title="Modelo de dados" icon="database" href="/l/pt/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Defina objetos, campos, papéis e relações.
</Card>
<Card title="Funções lógicas" icon="bolt" href="/l/pt/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
Funções no lado do servidor com gatilhos HTTP, cron e de eventos.
</Card>
<Card title="Componentes de front-end" icon="window-maximize" href="/l/pt/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Componentes React em sandbox dentro da UI do Twenty.
</Card>
<Card title="Layout" icon="table-columns" href="/l/pt/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Vistas, itens de navegação e layouts de página de registro.
</Card>
<Card title="Habilidades e agentes" icon="robot" href="/l/pt/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
Habilidades e agentes de IA com prompts personalizados.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/pt/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI e Testes" icon="terminal" href="/l/pt/developers/extend/apps/cli-and-testing">
Comandos de CLI, testes, assets, remotes e CI.
</Card>
<Card title="Publicação" icon="rocket" href="/l/pt/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Implante em um servidor ou publique no marketplace.
</Card>
</CardGroup>
@@ -20,7 +20,7 @@ Views, navigation items, and page layouts reference each other by `universalIden
* A **page layout** of type `RECORD_PAGE` targets an object and can embed [front components](/l/pt/developers/extend/apps/front-components) inside its tabs as widgets.
<AccordionGroup>
<Accordion title="defineView" description="Define visualizações salvas para objetos">
<Accordion title="defineView" description="Defina visualizações salvas para objetos">
As visualizações são configurações salvas de como os registros de um objeto são exibidos — incluindo quais campos são visíveis, sua ordem e quaisquer filtros ou grupos aplicados. Use `defineView()` para enviar visualizações pré-configuradas com seu app:
@@ -56,7 +56,7 @@ Pontos-chave:
* `position` controla a ordenação quando existem várias visualizações para o mesmo objeto.
</Accordion>
<Accordion title="defineNavigationMenuItem" description="Define links de navegação da barra lateral">
<Accordion title="defineNavigationMenuItem" description="Defina links de navegação da barra lateral">
Os itens do menu de navegação adicionam entradas personalizadas à barra lateral do espaço de trabalho. Use `defineNavigationMenuItem()` para vincular a visualizações, URLs externas ou objetos:
@@ -1,6 +1,6 @@
---
title: Habilidades e agentes
description: Define AI skills and agents for your app.
description: Defina habilidades e agentes de IA para o seu aplicativo.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. O recurso é funcional, mas ainda está evoluindo.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Os aplicativos podem definir capacidades de IA que residem dentro do espaço de trabalho — instruções de habilidades reutilizáveis e agentes com prompts de sistema personalizados.
<AccordionGroup>
<Accordion title="defineSkill" description="Defina habilidades de agentes de IA">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: chave
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: Fluxo de código de autorização com PKCE e credenciais de cliente para acesso servidor a servidor.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
A Twenty implementa OAuth 2.0 com código de autorização + PKCE para aplicações voltadas ao utilizador e credenciais de cliente para acesso servidor a servidor. Os clientes são registados dinamicamente via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — sem configuração manual num painel.
## When to Use OAuth
## Quando usar OAuth
| Cenário | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/pt/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/pt/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Cenário | Método de autenticação |
| -------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Scripts internos, automatização | [Chave de API](/l/pt/developers/extend/api#authentication) |
| Aplicação externa atuando em nome de um utilizador | **OAuth — Código de Autorização** |
| Servidor a servidor, sem contexto de utilizador | **OAuth — Credenciais do Cliente** |
| Aplicação Twenty com extensões de UI | [Aplicações](/l/pt/developers/extend/apps/getting-started) (OAuth é tratado automaticamente) |
## Register a Client
## Registar um cliente
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
A Twenty suporta **registo dinâmico de clientes** conforme [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). Sem necessidade de configuração manual — registe programaticamente:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Resposta:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
Guarde o `client_secret` com segurança — não poderá ser recuperado mais tarde.
</Warning>
## Escopos
| Scope | Acesso |
| -------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `perfil` | Read the authenticated user's profile information |
| Escopo | Acesso |
| -------- | ------------------------------------------------------- |
| `api` | Acesso total de leitura/escrita às APIs Core e Metadata |
| `perfil` | Leia as informações do perfil do utilizador autenticado |
Request scopes as a space-separated string: `scope=api profile`
Solicite escopos como uma cadeia separada por espaços: `scope=api profile`
## Authorization Code Flow
## Fluxo de Código de Autorização
Use this flow when your app acts on behalf of a Twenty user.
Use este fluxo quando a sua aplicação atuar em nome de um utilizador da Twenty.
### 1. Redirect the user to authorize
### 1. Redirecione o utilizador para autorizar
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Parâmetro | Obrigatório | Descrição |
| ----------------------- | ----------- | ------------------------------------------------------------ |
| `client_id` | Sim | Your registered client ID |
| `response_type` | Sim | Must be `code` |
| `redirect_uri` | Sim | Must match a registered redirect URI |
| `scope` | Não | Space-separated scopes (defaults to `api`) |
| `estado` | Recomendado | Random string to prevent CSRF attacks |
| `code_challenge` | Recomendado | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Recomendado | Must be `S256` when using PKCE |
| Parâmetro | Obrigatório | Descrição |
| ----------------------- | ----------- | ------------------------------------------------------------------- |
| `client_id` | Sim | O seu ID de cliente registado |
| `response_type` | Sim | Deve ser `code` |
| `redirect_uri` | Sim | Deve corresponder a um URI de redirecionamento registado |
| `scope` | Não | Escopos separados por espaços (predefinido para `api`) |
| `estado` | Recomendado | Cadeia aleatória para prevenir ataques CSRF |
| `code_challenge` | Recomendado | Desafio PKCE (hash SHA-256 do verificador, codificado em base64url) |
| `code_challenge_method` | Recomendado | Deve ser `S256` ao usar PKCE |
The user sees a consent screen and approves or denies access.
O utilizador vê um ecrã de consentimento e aprova ou nega o acesso.
### 2. Handle the callback
### 2. Trate o callback
After authorization, Twenty redirects back to your `redirect_uri`:
Após a autorização, a Twenty redireciona de volta para o seu `redirect_uri`:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
Verifique se `state` corresponde ao que enviou.
### 3. Exchange the code for tokens
### 3. Troque o código por tokens
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Resposta:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Use o token de acesso
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Atualize quando expirar
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## Fluxo de Credenciais do Cliente
For server-to-server integrations with no user interaction:
Para integrações servidor a servidor sem interação do utilizador:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
O token retornado tem acesso ao nível do espaço de trabalho, não vinculado a nenhum utilizador específico.
## Server Discovery
## Descoberta do servidor
Twenty publishes its OAuth configuration at a standard discovery endpoint:
A Twenty publica a sua configuração OAuth num endpoint padrão de descoberta:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Isto retorna todos os endpoints, tipos de concessão suportados, escopos e capacidades — útil para criar clientes OAuth genéricos.
## API Endpoints Summary
## Resumo de endpoints da API
| Endpoint | Finalidade |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Endpoint | Finalidade |
| ----------------------------------------- | ----------------------------------- |
| `/.well-known/oauth-authorization-server` | Descoberta de metadados do servidor |
| `/oauth/register` | Registo dinâmico de cliente |
| `/oauth/authorize` | Autorização do utilizador |
| `/oauth/token` | Troca e atualização de tokens |
| Ambiente | URL base |
| ------------------ | ------------------------ |
| **Nuvem** | `https://api.twenty.com` |
| **Auto-hospedado** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth vs Chaves de API
| | Chaves API | OAuth |
| ------------------ | ----------------------- | -------------------------------------- |
| **Configuração** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Melhor para** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Manual | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | Chaves API | OAuth |
| ----------------------------- | ------------------------------------ | ------------------------------------------------ |
| **Configuração** | Gerar em Configurações | Registar um cliente, implementar o fluxo |
| **Contexto do utilizador** | Nenhum (nível de espaço de trabalho) | Permissões de um utilizador específico |
| **Melhor para** | Scripts, ferramentas internas | Aplicações externas, integrações multiutilizador |
| **Rotação de tokens** | Manual | Automática via tokens de atualização |
| **Acesso baseado em escopos** | Acesso total à API | Granular por escopos |
@@ -1,12 +1,12 @@
---
title: Webhooks
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Receba notificações quando os registros mudarem — HTTP POST para o seu endpoint a cada criação, atualização ou exclusão.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Twenty envia um HTTP POST para sua URL sempre que um registro é criado, atualizado ou excluído. Todos os tipos de objeto estão cobertos, incluindo objetos personalizados.
## Criar Webhook
@@ -1,12 +1,12 @@
---
title: Commands
title: Команды
icon: terminal
description: Useful commands for developing Twenty.
description: Полезные команды для разработки Twenty.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
Команды можно запускать из корня репозитория с помощью `npx nx`. Используйте `npx nx run {project}:{command}` для явного указания цели.
## Starting the App
## Запуск приложения
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## База данных
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -22,7 +22,7 @@ npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
## Линтинг
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Проверка типов
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## Сборка
```bash
npx nx build twenty-shared # Must be built first
@@ -110,7 +110,7 @@ const value = process.env.MY_VALUE ?? 'default';
onClick?.();
```
## Называние
## Именование
* **Variables**: camelCase, descriptive (`email` not `value`, `fieldMetadata` not `fm`)
* **Constants**: SCREAMING_SNAKE_CASE
@@ -1,14 +1,14 @@
---
title: Архитектура
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Как работают приложения Twenty — изоляция в песочнице, жизненный цикл и базовые элементы.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Приложения Twenty — это пакеты TypeScript, которые расширяют ваше рабочее пространство пользовательскими объектами, логикой, компонентами интерфейса и возможностями ИИ. Они работают на платформе Twenty с полной изоляцией в песочнице и контролем прав доступа.
## How apps work
## Как работают приложения
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Приложение — это набор **сущностей**, объявленных с помощью функций `defineEntity()` из пакета `twenty-sdk`. SDK обнаруживает эти объявления посредством анализа AST на этапе сборки и формирует **манифест** — полное описание того, что ваше приложение добавляет в рабочее пространство.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**Организация файлов — на ваше усмотрение.** Обнаружение сущностей основано на AST — SDK находит вызовы `export default defineEntity(...)` независимо от расположения файла. Структура папок выше — это соглашение, а не требование.
</Note>
## Entity types
## Типы сущностей
| Сущность | Назначение | Документация |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------- |
| **Application** | App identity, permissions, variables | [Data Model](/l/ru/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/ru/developers/extend/apps/data-model) |
| **Object** | Custom data tables with fields | [Data Model](/l/ru/developers/extend/apps/data-model) |
| **Поле** | Extend existing objects, define relations | [Data Model](/l/ru/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Логические функции](/l/ru/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/ru/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/ru/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/ru/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/ru/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/ru/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/ru/developers/extend/apps/layout) |
| Сущность | Назначение | Документация |
| ------------------------ | ------------------------------------------------------------ | ---------------------------------------------------------------- |
| **Приложение** | Идентификация приложения, права доступа, переменные | [Модель данных](/l/ru/developers/extend/apps/data-model) |
| **Роль** | Наборы прав для объектов и полей | [Модель данных](/l/ru/developers/extend/apps/data-model) |
| **Object** | Пользовательские таблицы данных с полями | [Модель данных](/l/ru/developers/extend/apps/data-model) |
| **Поле** | Расширение существующих объектов, определение связей | [Модель данных](/l/ru/developers/extend/apps/data-model) |
| **Логическая функция** | Серверный TypeScript с триггерами | [Логические функции](/l/ru/developers/extend/apps/logic-functions) |
| **Компонент фронтенда** | Изолированный в песочнице интерфейс React на странице Twenty | [Компоненты фронтенда](/l/ru/developers/extend/apps/front-components) |
| **Навык** | Повторно используемые инструкции для ИИ-агента | [Навыки и агенты](/l/ru/developers/extend/apps/skills-and-agents) |
| **Агент** | ИИ-агенты с пользовательскими промптами | [Навыки и агенты](/l/ru/developers/extend/apps/skills-and-agents) |
| **Представление** | Преднастроенные представления списков записей | [Макет](/l/ru/developers/extend/apps/layout) |
| **Пункт меню навигации** | Пользовательские элементы боковой панели | [Макет](/l/ru/developers/extend/apps/layout) |
| **Макет страницы** | Пользовательские вкладки и виджеты страницы записи | [Макет](/l/ru/developers/extend/apps/layout) |
## Sandboxing
## Изоляция в песочнице
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Логические функции** выполняются в изолированных процессах Node.js на сервере. Они получают доступ к данным только через типизированный клиент API, ограниченный правами роли приложения.
* **Компоненты фронтенда** запускаются в Web Workers с использованием Remote DOM — изолированы от основной страницы, но при этом рендерят нативные элементы DOM (не iframes). Они взаимодействуют с Twenty через хостовый API обмена сообщениями.
* **Права доступа** применяются на уровне API. Токен времени выполнения (`TWENTY_APP_ACCESS_TOKEN`) выводится из роли, определённой в `defineApplication()`.
## App lifecycle
## Жизненный цикл приложения
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/ru/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — следит за исходными файлами и синхронизирует изменения в реальном времени с подключённым сервером Twenty. Типизированный клиент API автоматически пересоздаётся при изменении схемы.
* **`yarn twenty build`** — компилирует TypeScript, упаковывает логические функции и фронтенд-компоненты с помощью esbuild и формирует манифест.
* **Хуки до/после установки** — необязательные логические функции, которые выполняются во время установки. См. [Логические функции](/l/ru/developers/extend/apps/logic-functions) для подробностей.
## Следующие шаги
<CardGroup cols={2}>
<Card title="Модель данных" icon="database" href="/l/ru/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Определяйте объекты, поля, роли и связи.
</Card>
<Card title="Логические функции" icon="bolt" href="/l/ru/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
Серверные функции с HTTP-, cron- и событийными триггерами.
</Card>
<Card title="Компоненты фронтенда" icon="window-maximize" href="/l/ru/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Изолированные в песочнице компоненты React внутри интерфейса Twenty.
</Card>
<Card title="Макет" icon="table-columns" href="/l/ru/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Представления, пункты навигации и макеты страниц записей.
</Card>
<Card title="Навыки и агенты" icon="robot" href="/l/ru/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
ИИ-навыки и агенты с пользовательскими промптами.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/ru/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI и тестирование" icon="terminal" href="/l/ru/developers/extend/apps/cli-and-testing">
Команды CLI, тестирование, ассеты, удалённые модули и CI.
</Card>
<Card title="Публикация" icon="rocket" href="/l/ru/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Разверните на сервере или опубликуйте в маркетплейсе.
</Card>
</CardGroup>
@@ -1,6 +1,6 @@
---
title: CLI & Testing
description: CLI commands, testing setup, public assets, npm packages, remotes, and CI configuration.
title: CLI и тестирование
description: Команды CLI, настройка тестирования, публичные ресурсы, пакеты npm, удалённые репозитории и конфигурация CI.
icon: terminal
---
@@ -1,6 +1,6 @@
---
title: Навыки и агенты
description: Define AI skills and agents for your app.
description: Определите навыки и агентов ИИ для вашего приложения.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. Функция работает, но продолжает развиваться.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Приложения могут определять возможности ИИ, которые находятся внутри рабочего пространства — повторно используемые инструкции для навыков и агенты с настраиваемыми системными подсказками.
<AccordionGroup>
<Accordion title="defineSkill" description="Определяйте навыки ИИ-агентов">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: ключ
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: Поток авторизационного кода с PKCE и учётными данными клиента для доступа между серверами.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty реализует OAuth 2.0 с потоком авторизационного кода + PKCE для пользовательских приложений и с учётными данными клиента для доступа между серверами. Клиенты регистрируются динамически по [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — никакой ручной настройки в панели управления.
## When to Use OAuth
## Когда использовать OAuth
| Сценарий | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/ru/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/ru/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Сценарий | Метод аутентификации |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| Внутренние скрипты, автоматизация | [Ключ API](/l/ru/developers/extend/api#authentication) |
| Внешнее приложение, действующее от имени пользователя | **OAuth — авторизационный код** |
| Между серверами, без контекста пользователя | **OAuth — клиентские учётные данные** |
| Приложение Twenty с расширениями пользовательского интерфейса (UI) | [Приложения](/l/ru/developers/extend/apps/getting-started) (OAuth обрабатывается автоматически) |
## Register a Client
## Зарегистрировать клиента
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
Twenty поддерживает **динамическую регистрацию клиентов** по [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). Ручная настройка не требуется — регистрируйте программно:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Ответ:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
Храните `client_secret` в надёжном месте — позже его нельзя будет получить.
</Warning>
## Области действия
| Scope | Доступ |
| --------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `профиль` | Read the authenticated user's profile information |
| Область действия | Доступ |
| ---------------- | ----------------------------------------------------------- |
| `api` | Полный доступ на чтение/запись к Core и Metadata API |
| `profile` | Чтение информации профиля аутентифицированного пользователя |
Request scopes as a space-separated string: `scope=api profile`
Запрашивайте области действия как строку, разделённую пробелами: `scope=api profile`
## Authorization Code Flow
## Поток авторизационного кода
Use this flow when your app acts on behalf of a Twenty user.
Используйте этот поток, когда ваше приложение действует от имени пользователя Twenty.
### 1. Redirect the user to authorize
### 1. Перенаправьте пользователя для авторизации
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Параметр | Обязательно | Описание |
| ----------------------- | ------------- | ------------------------------------------------------------ |
| `client_id` | Да | Your registered client ID |
| `response_type` | Да | Must be `code` |
| `redirect_uri` | Да | Must match a registered redirect URI |
| `scope` | Нет | Space-separated scopes (defaults to `api`) |
| `состояние` | Рекомендуется | Random string to prevent CSRF attacks |
| `code_challenge` | Рекомендуется | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Рекомендуется | Must be `S256` when using PKCE |
| Параметр | Обязательно | Описание |
| ----------------------- | ------------- | --------------------------------------------------------------- |
| `client_id` | Да | Идентификатор вашего зарегистрированного клиента |
| `response_type` | Да | Должно быть `code` |
| `redirect_uri` | Да | Должен совпадать с зарегистрированным redirect URI |
| `scope` | Нет | Области действия, разделённые пробелами (по умолчанию `api`) |
| `state` | Рекомендуется | Случайная строка для предотвращения CSRF-атак |
| `code_challenge` | Рекомендуется | Вызов PKCE (хэш SHA-256 от верификатора, в кодировке base64url) |
| `code_challenge_method` | Рекомендуется | Должно быть `S256` при использовании PKCE |
The user sees a consent screen and approves or denies access.
Пользователь видит экран согласия и подтверждает или отклоняет доступ.
### 2. Handle the callback
### 2. Обработайте обратный вызов
After authorization, Twenty redirects back to your `redirect_uri`:
После авторизации Twenty перенаправляет обратно на ваш `redirect_uri`:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
Проверьте, что `state` совпадает с отправленным значением.
### 3. Exchange the code for tokens
### 3. Обменяйте код на токены
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Ответ:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Используйте токен доступа
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Обновляйте при истечении срока действия
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## Поток клиентских учётных данных
For server-to-server integrations with no user interaction:
Для интеграций между серверами без участия пользователя:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
Возвращаемый токен имеет доступ на уровне рабочей области и не привязан к конкретному пользователю.
## Server Discovery
## Обнаружение сервера
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty публикует свою конфигурацию OAuth на стандартной конечной точке обнаружения:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Это возвращает все конечные точки, поддерживаемые типы грантов, области действия и возможности — полезно для создания универсальных OAuth-клиентов.
## API Endpoints Summary
## Сводка конечных точек API
| Конечная точка | Назначение |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Конечная точка | Назначение |
| ----------------------------------------- | --------------------------------- |
| `/.well-known/oauth-authorization-server` | Обнаружение метаданных сервера |
| `/oauth/register` | Динамическая регистрация клиентов |
| `/oauth/authorize` | Авторизация пользователя |
| `/oauth/token` | Обмен и обновление токена |
| Среда | Базовый URL |
| --------------------------- | ------------------------ |
| **Облако** | `https://api.twenty.com` |
| **Самостоятельный хостинг** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth против ключей API
| | API ключи | OAuth |
| ---------------------------- | ----------------------- | -------------------------------------- |
| **Настройка** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Лучше всего подходит для** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Вручную | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API ключи | OAuth |
| ------------------------------- | ------------------------------------- | ----------------------------------------------------- |
| **Настройка** | Создаются в разделе «Настройки» | Зарегистрировать клиента, реализовать поток |
| **Контекст пользователя** | Отсутствует (уровень рабочей области) | Права конкретного пользователя |
| **Лучше всего подходит для** | Скрипты, внутренние инструменты | Внешние приложения, мультипользовательские интеграции |
| **Ротация токенов** | Вручную | Автоматическая с помощью refresh-токенов |
| **Доступ по областям действия** | Полный доступ к API | Детализированный через области действия |
@@ -1,12 +1,12 @@
---
title: Вебхуки
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Получайте уведомления при изменении записей — HTTP POST на вашу конечную точку при каждом создании, обновлении или удалении.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Twenty отправляет запрос HTTP POST на ваш URL каждый раз, когда запись создаётся, обновляется или удаляется. Поддерживаются все типы объектов, включая пользовательские объекты.
## Создать вебхук
@@ -1,12 +1,12 @@
---
title: Commands
title: Komutlar
icon: terminal
description: Useful commands for developing Twenty.
description: Twenty geliştirmek için yararlı komutlar.
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
Komutlar, deponun kök dizininden `npx nx` kullanılarak çalıştırılabilir. Açık hedefleme için `npx nx run {project}:{command}` kullanın.
## Starting the App
## Uygulamayı Başlatma
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## Veritabanı
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -22,7 +22,7 @@ npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
## Lint denetimi
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## Tip denetimi
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## Derleme
```bash
npx nx build twenty-shared # Must be built first
@@ -1,14 +1,14 @@
---
title: Mimari
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Twenty uygulamaları nasıl çalışır — korumalı alan, yaşam döngüsü ve yapı taşları.
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Twenty uygulamaları, çalışma alanınızı özel nesneler, mantık, UI bileşenleri ve yapay zekâ yetenekleriyle genişleten TypeScript paketleridir. Tam korumalı alan ve izin kontrolleriyle Twenty platformunda çalışırlar.
## How apps work
## Uygulamalar nasıl çalışır
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
Bir uygulama, `twenty-sdk` paketindeki `defineEntity()` işlevleri kullanılarak bildirilen **varlıklar** koleksiyonudur. SDK, bu bildirimleri derleme sırasında AST analiziyle algılar ve bir **manifest** üretir — uygulamanızın bir çalışma alanına neler eklediğinin eksiksiz bir açıklaması.
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**Dosya organizasyonu size kalmış.** Varlık algılama AST tabanlıdır — dosyanın nerede bulunduğundan bağımsız olarak SDK `export default defineEntity(...)` çağrılarını bulur. Yukarıdaki klasör yapısı bir gelenektir, zorunluluk değildir.
</Note>
## Entity types
## Varlık türleri
| Varlık | Amaç | Belgeler |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------- |
| **Application** | App identity, permissions, variables | [Data Model](/l/tr/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/tr/developers/extend/apps/data-model) |
| **Object** | Custom data tables with fields | [Data Model](/l/tr/developers/extend/apps/data-model) |
| **Alan** | Extend existing objects, define relations | [Data Model](/l/tr/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Mantıksal İşlevler](/l/tr/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/tr/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/tr/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/tr/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/tr/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/tr/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/tr/developers/extend/apps/layout) |
| Varlık | Amaç | Belgeler |
| ------------------------ | ------------------------------------------------------------- | --------------------------------------------------------------------- |
| **Uygulama** | Uygulama kimliği, izinler, değişkenler | [Veri Modeli](/l/tr/developers/extend/apps/data-model) |
| **Rol** | Nesneler ve alanlar için izin kümeleri | [Veri Modeli](/l/tr/developers/extend/apps/data-model) |
| **Object** | Alanlara sahip özel veri tabloları | [Veri Modeli](/l/tr/developers/extend/apps/data-model) |
| **Alan** | Mevcut nesneleri genişletme, ilişkileri tanımlama | [Veri Modeli](/l/tr/developers/extend/apps/data-model) |
| **Mantık İşlevi** | Tetikleyicilerle sunucu tarafı TypeScript | [Mantıksal İşlevler](/l/tr/developers/extend/apps/logic-functions) |
| **Ön Uç Bileşeni** | Twenty'nin sayfasında korumalı alanda React kullanıcı arayüzü | [Ön Uç Bileşenleri](/l/tr/developers/extend/apps/front-components) |
| **Beceri** | Yeniden kullanılabilir yapay zekâ temsilcisi yönergeleri | [Beceriler ve Temsilciler](/l/tr/developers/extend/apps/skills-and-agents) |
| **Temsilci** | Özel istemlere sahip yapay zekâ asistanları | [Beceriler ve Temsilciler](/l/tr/developers/extend/apps/skills-and-agents) |
| **Görünüm** | Önceden yapılandırılmış kayıt listesi görünümleri | [Düzen](/l/tr/developers/extend/apps/layout) |
| **Gezinme Menüsü Öğesi** | Özel kenar çubuğu öğeleri | [Düzen](/l/tr/developers/extend/apps/layout) |
| **Sayfa Düzeni** | Özel kayıt sayfası sekmeleri ve widget'lar | [Düzen](/l/tr/developers/extend/apps/layout) |
## Sandboxing
## Korumalı alan
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **Mantık işlevleri** sunucuda yalıtılmış Node.js işlemlerinde çalışır. Verilere yalnızca, kapsamı uygulamanın rol izinleriyle sınırlandırılmış tipli API istemcisi üzerinden erişirler.
* **Ön uç bileşenleri**, Remote DOM kullanan Web Worker'larda çalışır — ana sayfadan yalıtılmıştır ancak yerel DOM öğelerini (iframe'ler değil) oluşturur. Twenty ile mesaj iletimi yapan bir ana makine API'si aracılığıyla iletişim kurarlar.
* **İzinler**, API düzeyinde uygulanır. Çalışma zamanı belirteci (`TWENTY_APP_ACCESS_TOKEN`), `defineApplication()` içinde tanımlanan rolden türetilir.
## App lifecycle
## Uygulama yaşam döngüsü
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/tr/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — kaynak dosyalarınızı izler ve bağlı bir Twenty sunucusuna değişiklikleri canlı olarak senkronize eder. Şema değiştiğinde tipli API istemcisi otomatik olarak yeniden oluşturulur.
* **`yarn twenty build`** — TypeScript'i derler, mantık işlevlerini ve ön uç bileşenlerini esbuild ile paketler ve bir manifest üretir.
* **Kurulum öncesi/sonrası kancaları** — kurulum sırasında çalışan isteğe bağlı mantık işlevleri. Ayrıntılar için [Mantık İşlevleri](/l/tr/developers/extend/apps/logic-functions) bölümüne bakın.
## Sonraki adımlar
<CardGroup cols={2}>
<Card title="Veri modeli" icon="database" href="/l/tr/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
Nesneleri, alanları, rolleri ve ilişkileri tanımlayın.
</Card>
<Card title="Mantıksal işlevler" icon="bolt" href="/l/tr/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
HTTP, cron ve olay tetikleyicilerine sahip sunucu tarafı işlevler.
</Card>
<Card title="Ön uç bileşenleri" icon="window-maximize" href="/l/tr/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Twenty'nin kullanıcı arayüzünde korumalı alanda React bileşenleri.
</Card>
<Card title="Düzen" icon="table-columns" href="/l/tr/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
Görünümler, gezinme öğeleri ve kayıt sayfası düzenleri.
</Card>
<Card title="Beceriler ve Ajanlar" icon="robot" href="/l/tr/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
Özel istemlere sahip yapay zekâ becerileri ve temsilciler.
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/tr/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI ve Testler" icon="terminal" href="/l/tr/developers/extend/apps/cli-and-testing">
CLI komutları, test, varlıklar, uzak depolar ve CI.
</Card>
<Card title="Yayımlama" icon="rocket" href="/l/tr/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
Bir sunucuya dağıtın veya pazaryerine yayınlayın.
</Card>
</CardGroup>
@@ -1,6 +1,6 @@
---
title: CLI & Testing
description: CLI commands, testing setup, public assets, npm packages, remotes, and CI configuration.
title: CLI ve Testler
description: CLI komutları, test kurulumu, genel varlıklar, npm paketleri, uzaklar ve CI yapılandırması.
icon: terminal
---
@@ -1,6 +1,6 @@
---
title: Beceriler ve Ajanlar
description: Define AI skills and agents for your app.
description: Uygulamanız için yapay zekâ yetenekleri ve ajanları tanımlayın.
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. Özellik işlevsel ancak hâlâ gelişmekte.
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
Uygulamalar, çalışma alanıinde yer alan yapay zekâ yeteneklerini — yeniden kullanılabilir yetenek yönergeleri ve özel sistem istemlerine sahip ajanları — tanımlayabilir.
<AccordionGroup>
<Accordion title="defineSkill" description="Yapay zekâ ajanı yeteneklerini tanımlayın">
@@ -1,23 +1,23 @@
---
title: OAuth
icon: anahtar
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: PKCE'li yetkilendirme kodu akışı ve sunucudan sunucuya erişim için istemci kimlik bilgileri.
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty, kullanıcıya dönük uygulamalar için yetkilendirme kodu + PKCE'yi ve sunucudan sunucuya erişim için istemci kimlik bilgilerini kullanarak OAuth 2.0'ı uygular. İstemciler [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) aracılığıyla dinamik olarak kaydedilir — bir kontrol panelinde manuel kurulum gerekmez.
## When to Use OAuth
## OAuth Ne Zaman Kullanılır
| Senaryo | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/tr/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/tr/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| Senaryo | Kimlik Doğrulama Yöntemi |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Dahili betikler, otomasyon | [API Anahtarı](/l/tr/developers/extend/api#authentication) |
| Bir kullanıcının adına hareket eden harici uygulama | **OAuth — Yetkilendirme Kodu** |
| Sunucudan sunucuya, kullanıcı bağlamı yok | **OAuth — İstemci Kimlik Bilgileri** |
| UI uzantılarına sahip Twenty Uygulaması | [Uygulamalar](/l/tr/developers/extend/apps/getting-started) (OAuth otomatik olarak yönetilir) |
## Register a Client
## Bir İstemci Kaydedin
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
Twenty, [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) uyarınca **dinamik istemci kaydını** destekler. Manuel kurulum gerekmez — programatik olarak kaydedin:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**Yanıt:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
`client_secret` değerini güvenli bir şekilde saklayın — daha sonra geri alınamaz.
</Warning>
## Kapsamlar
| Scope | Erişim |
| -------- | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `profil` | Read the authenticated user's profile information |
| Kapsam | Erişim |
| --------- | ---------------------------------------------------------- |
| `api` | Core ve Metadata API'lerine tam okuma/yazma erişimi |
| `profile` | Kimliği doğrulanmış kullanıcının profil bilgilerini okuyun |
Request scopes as a space-separated string: `scope=api profile`
Kapsamları boşlukla ayrılmış bir dize olarak isteyin: `scope=api profile`
## Authorization Code Flow
## Yetkilendirme Kodu Akışı
Use this flow when your app acts on behalf of a Twenty user.
Uygulamanız bir Twenty kullanıcısı adına hareket ettiğinde bu akışı kullanın.
### 1. Redirect the user to authorize
### 1. Kullanıcıyı yetkilendirmek için yönlendirin
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| Parametre | Zorunlu | Açıklama |
| ----------------------- | -------- | ------------------------------------------------------------ |
| `client_id` | Evet | Your registered client ID |
| `response_type` | Evet | Must be `code` |
| `redirect_uri` | Evet | Must match a registered redirect URI |
| `scope` | Hayır | Space-separated scopes (defaults to `api`) |
| `durum` | Önerilen | Random string to prevent CSRF attacks |
| `code_challenge` | Önerilen | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | Önerilen | Must be `S256` when using PKCE |
| Parametre | Zorunlu | Açıklama |
| ----------------------- | -------- | ------------------------------------------------------------------------ |
| `client_id` | Evet | Kayıtlı istemci kimliğiniz |
| `response_type` | Evet | `code` olmalıdır |
| `redirect_uri` | Evet | Kayıtlı bir yönlendirme URI'siyle eşleşmelidir |
| `scope` | Hayır | Boşlukla ayrılmış kapsamlar (varsayılan: `api`) |
| `state` | Önerilen | CSRF saldırılarını önlemek için rastgele bir dize |
| `code_challenge` | Önerilen | PKCE challenge (doğrulayıcının SHA-256 karması, base64url ile kodlanmış) |
| `code_challenge_method` | Önerilen | PKCE kullanılırken `S256` olmalıdır |
The user sees a consent screen and approves or denies access.
Kullanıcı bir onay ekranı görür ve erişimi onaylar veya reddeder.
### 2. Handle the callback
### 2. Geri dönüşü işleyin
After authorization, Twenty redirects back to your `redirect_uri`:
Yetkilendirmeden sonra, Twenty `redirect_uri` adresinize geri yönlendirir:
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
`state` değerinin gönderdiğinizle eşleştiğini doğrulayın.
### 3. Exchange the code for tokens
### 3. Kodu belirteçlerle değiş tokuş edin
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**Yanıt:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. Erişim belirtecini kullanın
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. Süresi dolduğunda yenileyin
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## İstemci Kimlik Bilgileri Akışı
For server-to-server integrations with no user interaction:
Sunucudan sunucuya, kullanıcı etkileşimi olmayan entegrasyonlar için:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
Döndürülen belirteç, belirli bir kullanıcıya bağlı olmayan, çalışma alanı düzeyinde erişime sahiptir.
## Server Discovery
## Sunucu Keşfi
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty, OAuth yapılandırmasını standart bir keşif uç noktasında yayımlar:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
Bu, tüm uç noktaları, desteklenen grant türlerini, kapsamları ve yetenekleri döndürür — genel OAuth istemcileri oluşturmak için kullanışlıdır.
## API Endpoints Summary
## API Uç Noktaları Özeti
| Uç nokta | Amaç |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| Uç nokta | Amaç |
| ----------------------------------------- | ----------------------------- |
| `/.well-known/oauth-authorization-server` | Sunucu üstverisi keşfi |
| `/oauth/register` | Dinamik istemci kaydı |
| `/oauth/authorize` | Kullanıcı yetkilendirmesi |
| `/oauth/token` | Belirteç değişimi ve yenileme |
| Ortam | Temel URL |
| ---------------------------- | ------------------------ |
| **Bulut** | `https://api.twenty.com` |
| **Kendi Kendine Barındırma** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth ve API Anahtarları
| | API Anahtarları | OAuth |
| ------------------ | ----------------------- | -------------------------------------- |
| **Kurulum** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **En uygun** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | Manuel | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API Anahtarları | OAuth |
| ------------------------- | -------------------------- | -------------------------------------------------- |
| **Kurulum** | Ayarlar'da oluşturun | Bir istemci kaydedin, akışı uygulayın |
| **Kullanıcı bağlamı** | Yok (çalışma alanı düzeyi) | Belirli bir kullanıcının izinleri |
| **En uygun** | Betikler, dahili araçlar | Harici uygulamalar, çok kullanıcılı entegrasyonlar |
| **Belirteç döndürme** | Manuel | Yenileme belirteçleri aracılığıyla otomatik olarak |
| **Kapsama dayalı erişim** | Tam API erişimi | Kapsamlar aracılığıyla ayrıntılı |
@@ -1,12 +1,12 @@
---
title: Webhook'lar
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: Kayıtlar değiştiğinde bildirim alın — her oluşturma, güncelleme veya silme işleminde uç noktanıza HTTP POST gönderilir.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
Bir kayıt oluşturulduğunda, güncellendiğinde veya silindiğinde Twenty URL'inize bir HTTP POST gönderir. Özel nesneler dahil tüm nesne türleri kapsanır.
## Webhook oluştur
@@ -1,6 +1,6 @@
---
title: Sorun Giderme
icon: wrench
icon: anahtar
---
## Sorun Giderme
@@ -1,6 +1,6 @@
---
title: İkonlar
icon: i̇konlar
icon: ikonlar
---
<Frame>
@@ -9,13 +9,13 @@ Twenty'nin yerleşimi üç düzeyde özelleştirilebilir: uygulamada nasıl gezi
Sol kenar çubuğu tamamen özelleştirilebilir. Şunları yapabilirsiniz:
* **Öğeleri sürükleyip bırakarak yeniden sıralayın**
* **Öğeleri yeniden sıralayın** sürükleyip bırakarak
* **Klasörler oluşturun** ilgili nesneleri ve görünümleri gruplamak için
* **Nesneleri gizleyin** — kullanmadıklarınızı
* **Özel bağlantılar ekleyin** harici araçlara
* **Favorileri sabitleyin** görünümlere, kayıtlara veya aramalara hızlı erişim için
[Gezinme başvurusu →](/l/tr/user-guide/layout/capabilities/navigation)
[Gezinme referansı →](/l/tr/user-guide/layout/capabilities/navigation)
## Görünümler
@@ -42,4 +42,4 @@ Bir kaydı açtığınızda, ayrıntı sayfası yapılandırılabilir sekmeler v
Komut menüsünden yerleşim özelleştirme moduna girin (`Cmd+K` → "Kayıt sayfası yerleşimini düzenle").
[Kayıt sayfaları başvurusu →](/l/tr/user-guide/layout/capabilities/record-pages)
[Kayıt sayfaları referansı →](/l/tr/user-guide/layout/capabilities/record-pages)
@@ -1,12 +1,12 @@
---
title: Commands
title: 命令
icon: terminal
description: Useful commands for developing Twenty.
description: 用于开发 Twenty 的实用命令。
---
Commands can be run from the repository root using `npx nx`. Use `npx nx run {project}:{command}` for explicit targeting.
可以在存储库根目录使用 `npx nx` 运行命令。 使用 `npx nx run {project}:{command}` 显式指定目标。
## Starting the App
## 启动应用
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
@@ -14,7 +14,7 @@ npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Database
## 数据库
```bash
npx nx database:reset twenty-server # Reset and seed database
@@ -22,7 +22,7 @@ npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
## 代码检查
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
@@ -30,7 +30,7 @@ npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Type Checking
## 类型检查
```bash
npx nx typecheck twenty-front
@@ -68,7 +68,7 @@ npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
## 构建
```bash
npx nx build twenty-shared # Must be built first
@@ -1,14 +1,14 @@
---
title: 架构
description: How Twenty apps work — sandboxing, lifecycle, and the building blocks.
description: Twenty 应用如何运作——沙盒化、生命周期与构建模块。
icon: sitemap
---
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
Twenty 应用是 TypeScript 包,可通过自定义对象、逻辑、UI 组件和 AI 能力扩展你的工作区。 它们在 Twenty 平台上运行,具备完备的沙盒与权限控制。
## How apps work
## 应用如何运作
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace.
应用是由 `twenty-sdk` 包中的 `defineEntity()` 函数声明的**实体**集合。 SDK 在构建时通过 AST 分析检测到这些声明,并生成一份**清单**——完整描述你的应用为工作区新增的内容。
```
your-app/
@@ -29,32 +29,32 @@ your-app/
```
<Note>
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
**文件组织由你决定。** 实体检测基于 AST——无论文件位于何处,SDK 都能找到 `export default defineEntity(...)` 的调用。 上述文件夹结构是一种约定,而非强制要求。
</Note>
## Entity types
## 实体类型
| 实体 | 目的 | 文档 |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| **Application** | App identity, permissions, variables | [Data Model](/l/zh/developers/extend/apps/data-model) |
| **Role** | Permission sets for objects and fields | [Data Model](/l/zh/developers/extend/apps/data-model) |
| **对象** | Custom data tables with fields | [Data Model](/l/zh/developers/extend/apps/data-model) |
| **字段** | Extend existing objects, define relations | [Data Model](/l/zh/developers/extend/apps/data-model) |
| **Logic Function** | Server-side TypeScript with triggers | [Logic Functions](/l/zh/developers/extend/apps/logic-functions) |
| **Front Component** | Sandboxed React UI in Twenty's page | [Front Components](/l/zh/developers/extend/apps/front-components) |
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/zh/developers/extend/apps/skills-and-agents) |
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/zh/developers/extend/apps/skills-and-agents) |
| **View** | Pre-configured record list views | [Layout](/l/zh/developers/extend/apps/layout) |
| **Navigation Menu Item** | Custom sidebar entries | [Layout](/l/zh/developers/extend/apps/layout) |
| **Page Layout** | Custom record page tabs and widgets | [Layout](/l/zh/developers/extend/apps/layout) |
| 实体 | 目的 | 文档 |
| --------- | ------------------------- | -------------------------------------------------- |
| **应用程序** | 应用标识、权限、变量 | [数据模型](/l/zh/developers/extend/apps/data-model) |
| **角色** | 对象和字段的权限集 | [数据模型](/l/zh/developers/extend/apps/data-model) |
| **对象** | 带字段的自定义数据表 | [数据模型](/l/zh/developers/extend/apps/data-model) |
| **字段** | 扩展现有对象,定义关系 | [数据模型](/l/zh/developers/extend/apps/data-model) |
| **逻辑函数** | 带触发器的服务端 TypeScript | [逻辑函数](/l/zh/developers/extend/apps/logic-functions) |
| **前端组件** | 在 Twenty 页面中的沙盒化 React UI | [前端组件](/l/zh/developers/extend/apps/front-components) |
| **技能** | 可复用的 AI 代理指令 | [技能与代理](/l/zh/developers/extend/apps/skills-and-agents) |
| **代理** | 具有自定义提示词的 AI 助手 | [技能与代理](/l/zh/developers/extend/apps/skills-and-agents) |
| **视图** | 预配置的记录列表视图 | [布局](/l/zh/developers/extend/apps/layout) |
| **导航菜单项** | 自定义侧边栏条目 | [布局](/l/zh/developers/extend/apps/layout) |
| **页面布局** | 自定义记录页面的选项卡和小部件 | [布局](/l/zh/developers/extend/apps/layout) |
## Sandboxing
## 沙盒化
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
* **逻辑函数** 在服务器上的独立 Node.js 进程中运行。 它们只能通过类型化的 API 客户端访问数据,且范围受应用角色权限限制。
* **前端组件** 在使用 Remote DOM 的 Web Worker 中运行——与主页面沙盒隔离,但渲染原生 DOM 元素(非 iframe)。 它们通过消息传递的宿主 API 与 Twenty 通信。
* **权限** 在 API 层面强制执行。 运行时令牌(`TWENTY_APP_ACCESS_TOKEN`)源自 `defineApplication()` 中定义的角色。
## App lifecycle
## 应用生命周期
```
┌─────────────────────────────────────────────────────────┐
@@ -73,32 +73,32 @@ your-app/
└─────────────────────────────────────────────────────────┘
```
* **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
* **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
* **Pre/post-install hooks** — optional logic functions that run during installation. See [Logic Functions](/l/zh/developers/extend/apps/logic-functions) for details.
* **`yarn twenty dev`** — 监视你的源文件,并将更改实时同步到已连接的 Twenty 服务器。 当模式发生变化时,会自动重新生成类型化的 API 客户端。
* **`yarn twenty build`** — 编译 TypeScript,使用 esbuild 打包逻辑函数和前端组件,并生成清单。
* **预/后安装钩子** — 在安装过程中运行的可选逻辑函数。 详情请参见[逻辑函数](/l/zh/developers/extend/apps/logic-functions)
## Next steps
## 后续步骤
<CardGroup cols={2}>
<Card title="数据模型" icon="database" href="/l/zh/developers/extend/apps/data-model">
Define objects, fields, roles, and relations.
定义对象、字段、角色和关系。
</Card>
<Card title="逻辑函数" icon="bolt" href="/l/zh/developers/extend/apps/logic-functions">
Server-side functions with HTTP, cron, and event triggers.
具有 HTTPcron 和事件触发器的服务端函数。
</Card>
<Card title="前端组件" icon="window-maximize" href="/l/zh/developers/extend/apps/front-components">
Sandboxed React components inside Twenty's UI.
Twenty UI 中的沙盒化 React 组件。
</Card>
<Card title="布局" icon="table-columns" href="/l/zh/developers/extend/apps/layout">
Views, navigation items, and record page layouts.
视图、导航菜单项和记录页面布局。
</Card>
<Card title="技能与智能体" icon="robot" href="/l/zh/developers/extend/apps/skills-and-agents">
AI skills and agents with custom prompts.
具有自定义提示词的 AI 技能与代理。
</Card>
<Card title="CLI & Testing" icon="terminal" href="/l/zh/developers/extend/apps/cli-and-testing">
CLI commands, testing, assets, remotes, and CI.
<Card title="CLI 与测试" icon="terminal" href="/l/zh/developers/extend/apps/cli-and-testing">
CLI 命令、测试、资源、远程和 CI
</Card>
<Card title="发布" icon="rocket" href="/l/zh/developers/extend/apps/publishing">
Deploy to a server or publish to the marketplace.
部署到服务器或发布到应用市场。
</Card>
</CardGroup>
@@ -1,6 +1,6 @@
---
title: 技能与智能体
description: Define AI skills and agents for your app.
description: 为你的应用定义 AI 技能和智能体。
icon: robot
---
@@ -8,7 +8,7 @@ icon: robot
Skills and agents are currently in alpha. 该功能可用,但仍在演进中。
</Warning>
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
应用可以定义存在于工作区内的 AI 能力——可复用的技能指令以及具有自定义系统提示词的智能体。
<AccordionGroup>
<Accordion title="defineSkill" description="定义 AI 智能体技能">
@@ -40,9 +40,9 @@ export default defineSkill({
* `description`(可选)提供有关技能用途的更多上下文。
</Accordion>
<Accordion title="defineAgent" description="Define AI agents with custom prompts">
<Accordion title="defineAgent" description="使用自定义提示词定义 AI 智能体">
Agents are AI assistants that live inside your workspace. Use `defineAgent()` to create agents with a custom system prompt:
智能体是在你的工作区内驻留的 AI 助手。 使用 `defineAgent()` 来创建带有自定义系统提示词的智能体:
```ts src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk/define';
@@ -58,12 +58,12 @@ export default defineAgent({
```
关键点:
* `name` is the unique identifier string for the agent (kebab-case recommended).
* `label` is the display name shown in the UI.
* `prompt` is the system prompt that defines the agent's behavior.
* `description` (optional) provides context about what the agent does.
* `name` 是该智能体的唯一标识字符串(推荐使用 kebab-case)。
* `label` 是在 UI 中显示的名称。
* `prompt` 是定义智能体行为的系统提示词。
* `description`(可选)提供有关智能体功能的上下文。
* `icon`(可选)设置在 UI 中显示的图标。
* `modelId` (optional) overrides the default AI model used by the agent.
* `modelId`(可选)会覆盖该智能体使用的默认 AI 模型。
</Accordion>
</AccordionGroup>
@@ -1,23 +1,23 @@
---
title: OAuth
icon: 键
description: Authorization code flow with PKCE and client credentials for server-to-server access.
description: 采用 PKCE 的授权码流程和用于服务器到服务器访问的客户端凭证。
---
Twenty implements OAuth 2.0 with authorization code + PKCE for user-facing apps and client credentials for server-to-server access. Clients are registered dynamically via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — no manual setup in a dashboard.
Twenty 对面向用户的应用采用授权码 + PKCE 实现 OAuth 2.0,对服务器到服务器访问采用客户端凭证。 客户端通过 [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) 动态注册——无需在仪表板中手动设置。
## When to Use OAuth
## 何时使用 OAuth
| 场景 | Auth Method |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Internal scripts, automation | [API Key](/l/zh/developers/extend/api#authentication) |
| External app acting on behalf of a user | **OAuth — Authorization Code** |
| Server-to-server, no user context | **OAuth — Client Credentials** |
| Twenty App with UI extensions | [Apps](/l/zh/developers/extend/apps/getting-started) (OAuth is handled automatically) |
| 场景 | 认证方式 |
| ------------------- | ----------------------------------------------------------- |
| 内部脚本、自动化 | [API 密钥](/l/zh/developers/extend/api#authentication) |
| 代表用户执行操作的外部应用 | **OAuth — 授权码** |
| 服务器到服务器,无用户上下文 | **OAuth — 客户端凭证** |
| 带有 UI 扩展的 Twenty 应用 | [应用](/l/zh/developers/extend/apps/getting-started) (OAuth 将自动处理) |
## Register a Client
## 注册客户端
Twenty supports **dynamic client registration** per [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No manual setup needed — register programmatically:
根据 [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591),Twenty 支持**动态客户端注册**。 无需手动设置——以编程方式注册:
```bash
POST /oauth/register
@@ -31,7 +31,7 @@ Content-Type: application/json
}
```
**Response:**
**响应:**
```json
{
@@ -43,23 +43,23 @@ Content-Type: application/json
```
<Warning>
Store the `client_secret` securely — it cannot be retrieved later.
安全存储 `client_secret`——之后无法再检索。
</Warning>
## 范围
| Scope | 访问 |
| ------ | ---------------------------------------------------- |
| `api` | Full read/write access to the Core and Metadata APIs |
| `个人资料` | Read the authenticated user's profile information |
| 范围 | 访问 |
| ------ | ------------------------------ |
| `api` | Core Metadata API 的完全读/写访问 |
| `个人资料` | 读取已认证用户的个人资料信息 |
Request scopes as a space-separated string: `scope=api profile`
以空格分隔的字符串请求范围:`scope=api profile`
## Authorization Code Flow
## 授权码流程
Use this flow when your app acts on behalf of a Twenty user.
当你的应用代表某个 Twenty 用户执行操作时,请使用此流程。
### 1. Redirect the user to authorize
### 1. 重定向用户以进行授权
```
GET /oauth/authorize?
@@ -72,29 +72,29 @@ GET /oauth/authorize?
code_challenge_method=S256
```
| 参数 | 必填 | 描述 |
| ----------------------- | -- | ------------------------------------------------------------ |
| `client_id` | 是 | Your registered client ID |
| `response_type` | 是 | Must be `code` |
| `redirect_uri` | 是 | Must match a registered redirect URI |
| `scope` | 否 | Space-separated scopes (defaults to `api`) |
| `状态` | 推荐 | Random string to prevent CSRF attacks |
| `code_challenge` | 推荐 | PKCE challenge (SHA-256 hash of verifier, base64url-encoded) |
| `code_challenge_method` | 推荐 | Must be `S256` when using PKCE |
| 参数 | 必填 | 描述 |
| ----------------------- | -- | ------------------------------------- |
| `client_id` | 是 | 你已注册的客户端 ID |
| `response_type` | 是 | 必须为 `code` |
| `redirect_uri` | 是 | 必须与已注册的重定向 URI 匹配 |
| `scope` | 否 | 以空格分隔的范围(默认为 `api` |
| `状态` | 推荐 | 用于防止 CSRF 攻击的随机字符串 |
| `code_challenge` | 推荐 | PKCE 质询(验证器的 SHA-256 哈希,base64url 编码) |
| `code_challenge_method` | 推荐 | 使用 PKCE 时必须为 `S256` |
The user sees a consent screen and approves or denies access.
用户将看到授权同意界面,并同意或拒绝访问。
### 2. Handle the callback
### 2. 处理回调
After authorization, Twenty redirects back to your `redirect_uri`:
授权后,Twenty 会重定向回你的 `redirect_uri`
```
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
```
Verify that `state` matches what you sent.
验证 `state` 与你发送的值一致。
### 3. Exchange the code for tokens
### 3. 将授权码交换为令牌
```bash
POST /oauth/token
@@ -108,7 +108,7 @@ client_secret=YOUR_CLIENT_SECRET&
code_verifier=YOUR_PKCE_VERIFIER
```
**Response:**
**响应:**
```json
{
@@ -119,14 +119,14 @@ code_verifier=YOUR_PKCE_VERIFIER
}
```
### 4. Use the access token
### 4. 使用访问令牌
```bash
GET /rest/companies
Authorization: Bearer ACCESS_TOKEN
```
### 5. Refresh when expired
### 5. 过期时刷新
```bash
POST /oauth/token
@@ -138,9 +138,9 @@ client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
```
## Client Credentials Flow
## 客户端凭证流程
For server-to-server integrations with no user interaction:
适用于无用户交互的服务器到服务器集成:
```bash
POST /oauth/token
@@ -152,38 +152,38 @@ client_secret=YOUR_CLIENT_SECRET&
scope=api
```
The returned token has workspace-level access, not tied to any specific user.
返回的令牌具有工作区级访问权限,不绑定到任何特定用户。
## Server Discovery
## 服务器发现
Twenty publishes its OAuth configuration at a standard discovery endpoint:
Twenty 在标准发现端点发布其 OAuth 配置:
```
GET /.well-known/oauth-authorization-server
```
This returns all endpoints, supported grant types, scopes, and capabilities — useful for building generic OAuth clients.
这将返回所有端点、支持的授权类型、范围和功能——有助于构建通用的 OAuth 客户端。
## API Endpoints Summary
## API 端点概览
| 端点 | 目的 |
| ----------------------------------------- | --------------------------- |
| `/.well-known/oauth-authorization-server` | Server metadata discovery |
| `/oauth/register` | Dynamic client registration |
| `/oauth/authorize` | User authorization |
| `/oauth/token` | Token exchange and refresh |
| 端点 | 目的 |
| ----------------------------------------- | -------- |
| `/.well-known/oauth-authorization-server` | 服务器元数据发现 |
| `/oauth/register` | 动态客户端注册 |
| `/oauth/authorize` | 用户授权 |
| `/oauth/token` | 令牌交换与刷新 |
| 环境 | 基础 URL |
| ------- | ------------------------ |
| **云端** | `https://api.twenty.com` |
| **自托管** | `https://{your-domain}` |
## OAuth vs API Keys
## OAuth API 密钥对比
| | API 密钥 | OAuth |
| ------------------ | ----------------------- | -------------------------------------- |
| **Setup** | Generate in Settings | Register a client, implement flow |
| **User context** | None (workspace-level) | Specific user's permissions |
| **Best for** | Scripts, internal tools | External apps, multi-user integrations |
| **Token rotation** | 手动 | Automatic via refresh tokens |
| **Scoped access** | Full API access | Granular via scopes |
| | API 密钥 | OAuth |
| ----------- | ---------- | ----------- |
| **设置** | 在设置中生成 | 注册客户端并实现流程 |
| **用户上下文** | 无(工作区级) | 特定用户的权限 |
| **最适合** | 脚本、内部工具 | 外部应用、多用户集成 |
| **令牌轮换** | 手动 | 通过刷新令牌自动完成 |
| **范围限定的访问** | 完整的 API 访问 | 通过范围实现细粒度控制 |
@@ -1,12 +1,12 @@
---
title: Webhooks
icon: satellite-dish
description: Get notified when records change — HTTP POST to your endpoint on every create, update, or delete.
description: 当记录更改时收到通知 — 在每次创建、更新或删除时向您的端点发送 HTTP POST。
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty sends an HTTP POST to your URL whenever a record is created, updated, or deleted. All object types are covered, including custom objects.
每当记录被创建、更新或删除时,Twenty 会向您的 URL 发送 HTTP POST。 涵盖所有对象类型,包括自定义对象。
## 创建 Webhook
+1 -1
View File
@@ -61,7 +61,7 @@ const jestConfig = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: {
global: {
statements: 48,
statements: 47.9,
lines: 46,
functions: 39.5,
},
@@ -1,4 +1,5 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -42,11 +43,13 @@ export const useNavigationMenuItemFolderOpenState = ({
lastClickedNavigationMenuItemIdState,
);
const [isManuallyClosed, setIsManuallyClosed] = useState(false);
const isExplicitlyOpen = openNavigationMenuItemFolderIds.includes(folderId);
const hasActiveChild = folderChildrenNavigationMenuItems.some((item) =>
activeNavigationMenuItemIds.includes(item.id),
);
const isOpen = isExplicitlyOpen || hasActiveChild;
const isOpen = isExplicitlyOpen || (hasActiveChild && !isManuallyClosed);
const handleToggle = () => {
if (isMobile) {
@@ -55,10 +58,13 @@ export const useNavigationMenuItemFolderOpenState = ({
);
} else {
setOpenNavigationMenuItemFolderIds((current) =>
current.includes(folderId)
isOpen
? current.filter((id) => id !== folderId)
: [...current, folderId],
: current.includes(folderId)
? current
: [...current, folderId],
);
setIsManuallyClosed(isOpen);
}
if (!isOpen) {
@@ -12,9 +12,7 @@ import { type SpreadsheetColumns } from '@/spreadsheet-import/types/SpreadsheetC
import { SpreadsheetColumnType } from '@/spreadsheet-import/types/SpreadsheetColumnType';
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
import { ModalContent } from 'twenty-ui/layout';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { Trans, useLingui } from '@lingui/react/macro';
import {
type Dispatch,
@@ -23,6 +21,8 @@ import {
useMemo,
useState,
} from 'react';
import { ModalContent } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
// @ts-expect-error Todo: remove usage of react-data-grid`
import { type RowsChangeData } from 'react-data-grid';
import { isDefined } from 'twenty-shared/utils';
@@ -80,6 +80,7 @@ const StyledScrollContainer = styled.div`
flex-direction: column;
flex-grow: 1;
height: 0px;
overflow: auto;
width: 100%;
`;
@@ -1,11 +1,18 @@
import { useStore } from 'jotai';
import { useEffect, useRef } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { isSelectableListGridFocusedState } from '@/ui/layout/selectable-list/states/isSelectableListGridFocusedState';
export const useInputFocusWithoutScrollOnMount = () => {
const inputRef = useRef<HTMLInputElement>(null);
const store = useStore();
useEffect(() => {
if (isDefined(inputRef.current)) {
if (
isDefined(inputRef.current) &&
!store.get(isSelectableListGridFocusedState.atom)
) {
inputRef.current.focus({ preventScroll: true });
}
});
@@ -1,8 +1,10 @@
import { useStore } from 'jotai';
import { type ReactNode, useEffect } from 'react';
import { useSelectableListHotKeys } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys';
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
import { SelectableListContextProvider } from '@/ui/layout/selectable-list/states/contexts/SelectableListContext';
import { isSelectableListGridFocusedState } from '@/ui/layout/selectable-list/states/isSelectableListGridFocusedState';
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { isDefined } from 'twenty-shared/utils';
@@ -27,6 +29,7 @@ export const SelectableList = ({
}: SelectableListProps) => {
useSelectableListHotKeys(selectableListInstanceId, focusId, onSelect);
const store = useStore();
const setSelectableItemIds = useSetAtomComponentState(
selectableItemIdsComponentState,
selectableListInstanceId,
@@ -48,6 +51,12 @@ export const SelectableList = ({
}
}, [selectableItemIdArray, selectableItemIdMatrix, setSelectableItemIds]);
useEffect(() => {
return () => {
store.set(isSelectableListGridFocusedState.atom, false);
};
}, [store]);
return (
<SelectableListComponentInstanceContext.Provider
value={{
@@ -1,11 +1,12 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { useCallback, useRef } from 'react';
import { Key } from 'ts-key-enum';
import { isSelectableListGridFocusedState } from '@/ui/layout/selectable-list/states/isSelectableListGridFocusedState';
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
type Direction = 'up' | 'down' | 'left' | 'right';
@@ -15,6 +16,41 @@ export const useSelectableListHotKeys = (
focusId: string,
onSelect?: (itemId: string) => void,
) => {
// oxlint-disable-next-line twenty/no-state-useref
const lastBlurredInputRef = useRef<HTMLInputElement | null>(null);
const store = useStore();
const blurActiveInputIfNeeded = () => {
if (document.activeElement instanceof HTMLInputElement) {
lastBlurredInputRef.current = document.activeElement;
store.set(isSelectableListGridFocusedState.atom, true);
document.activeElement.blur();
}
};
const refocusBlurredInput = () => {
if (!lastBlurredInputRef.current) {
return;
}
store.set(isSelectableListGridFocusedState.atom, false);
lastBlurredInputRef.current.focus();
lastBlurredInputRef.current = null;
};
const clearSelection = (selectedItemId: string | null) => {
if (isNonEmptyString(selectedItemId)) {
store.set(selectedItemIdComponentState.atomFamily({ instanceId }), null);
store.set(
isSelectedItemIdComponentFamilyState.atomFamily({
instanceId,
familyKey: selectedItemId,
}),
false,
);
}
};
const findPosition = (
selectableItemIds: string[][],
selectedItemId?: string | null,
@@ -31,8 +67,6 @@ export const useSelectableListHotKeys = (
}
};
const store = useStore();
const handleSelect = useCallback(
(direction: Direction) => {
const selectedItemId = store.get(
@@ -138,16 +172,58 @@ export const useSelectableListHotKeys = (
useHotkeysOnFocusedElement({
keys: Key.ArrowUp,
callback: () => handleSelect('up'),
callback: () => {
blurActiveInputIfNeeded();
const selectedItemId = store.get(
selectedItemIdComponentState.atomFamily({ instanceId }),
);
const selectableItemIds = store.get(
selectableItemIdsComponentState.atomFamily({ instanceId }),
);
const position = findPosition(selectableItemIds, selectedItemId);
const isAtTop = position !== undefined && position.row === 0;
if (!isAtTop || !lastBlurredInputRef.current) {
handleSelect('up');
return;
}
clearSelection(selectedItemId);
refocusBlurredInput();
},
focusId,
dependencies: [handleSelect, store, instanceId],
});
useHotkeysOnFocusedElement({
keys: Key.ArrowDown,
callback: () => {
blurActiveInputIfNeeded();
handleSelect('down');
},
focusId,
dependencies: [handleSelect],
});
useHotkeysOnFocusedElement({
keys: Key.ArrowDown,
callback: () => handleSelect('down'),
keys: '*',
callback: (keyboardEvent) => {
if (keyboardEvent.key.length !== 1) {
return;
}
if (
keyboardEvent.metaKey ||
keyboardEvent.ctrlKey ||
keyboardEvent.altKey
) {
return;
}
refocusBlurredInput();
},
focusId,
dependencies: [handleSelect],
dependencies: [],
options: { enableOnFormTags: false, preventDefault: false },
});
useHotkeysOnFocusedElement({
@@ -155,6 +231,7 @@ export const useSelectableListHotKeys = (
callback: () => handleSelect('left'),
focusId,
dependencies: [handleSelect],
options: { enableOnFormTags: false },
});
useHotkeysOnFocusedElement({
@@ -162,5 +239,6 @@ export const useSelectableListHotKeys = (
callback: () => handleSelect('right'),
focusId,
dependencies: [handleSelect],
options: { enableOnFormTags: false },
});
};
@@ -0,0 +1,6 @@
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
export const isSelectableListGridFocusedState = createAtomState<boolean>({
key: 'isSelectableListGridFocusedState',
defaultValue: false,
});
@@ -1,6 +1,6 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { getDefaultObjectFields } from '@/cli/utilities/build/manifest/utils/get-default-object-fields';
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import { generateDefaultFieldUniversalIdentifier } from '@/sdk/define/objects/generate-default-field-universal-identifier';
import type { ObjectConfig } from '@/sdk/define/objects/object-config';
const mockObjectConfig: ObjectConfig = {
@@ -14,7 +14,7 @@ const mockObjectConfig: ObjectConfig = {
const expectedUniversalId = (fieldName: string) =>
generateDefaultFieldUniversalIdentifier({
objectConfig: mockObjectConfig,
objectUniversalIdentifier: mockObjectConfig.universalIdentifier,
fieldName,
});
@@ -1,11 +1,13 @@
import type { ObjectConfig } from '@/sdk/define/objects/object-config';
import { FieldMetadataType } from 'twenty-shared/types';
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import { generateDefaultFieldUniversalIdentifier } from '@/sdk/define/objects/generate-default-field-universal-identifier';
import { type ObjectFieldManifest } from 'twenty-shared/application';
export const getDefaultObjectFields = (
objectConfig: ObjectConfig,
): ObjectFieldManifest[] => {
const objectUniversalIdentifier = objectConfig.universalIdentifier;
const idField: ObjectFieldManifest = {
name: 'id',
label: 'Id',
@@ -15,7 +17,7 @@ export const getDefaultObjectFields = (
defaultValue: 'uuid',
type: FieldMetadataType.UUID as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'id',
}),
};
@@ -29,7 +31,7 @@ export const getDefaultObjectFields = (
defaultValue: null,
type: FieldMetadataType.TEXT as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'name',
}),
};
@@ -43,7 +45,7 @@ export const getDefaultObjectFields = (
defaultValue: 'now',
type: FieldMetadataType.DATE_TIME as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'createdAt',
}),
};
@@ -57,7 +59,7 @@ export const getDefaultObjectFields = (
defaultValue: 'now',
type: FieldMetadataType.DATE_TIME as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'updatedAt',
}),
};
@@ -71,7 +73,7 @@ export const getDefaultObjectFields = (
defaultValue: null,
type: FieldMetadataType.DATE_TIME as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'deletedAt',
}),
};
@@ -85,7 +87,7 @@ export const getDefaultObjectFields = (
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'createdBy',
}),
};
@@ -99,7 +101,7 @@ export const getDefaultObjectFields = (
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR as const,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'updatedBy',
}),
};
@@ -113,7 +115,7 @@ export const getDefaultObjectFields = (
defaultValue: 0,
type: FieldMetadataType.POSITION,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'position',
}),
};
@@ -127,7 +129,7 @@ export const getDefaultObjectFields = (
defaultValue: null,
type: FieldMetadataType.TS_VECTOR,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'searchVector',
}),
};
@@ -1,4 +1,4 @@
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import { generateDefaultFieldUniversalIdentifier } from '@/sdk/define/objects/generate-default-field-universal-identifier';
import { RelationType } from '@/sdk/define';
import type { ObjectConfig } from '@/sdk/define/objects/object-config';
import {
@@ -130,13 +130,13 @@ export const getDefaultRelationObjectFields = (
const forwardFieldUniversalIdentifier =
generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier: objectConfig.universalIdentifier,
fieldName: config.fieldName,
});
const reverseFieldUniversalIdentifier =
generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier: objectConfig.universalIdentifier,
fieldName: `${config.fieldName}Inverse`,
});
@@ -63,6 +63,7 @@ export type { InputJsonSchema } from 'twenty-shared/logic-function';
export { defineNavigationMenuItem } from '@/sdk/define/navigation-menu-items/define-navigation-menu-item';
export { defineObject } from '@/sdk/define/objects/define-object';
export { generateDefaultFieldUniversalIdentifier } from '@/sdk/define/objects/generate-default-field-universal-identifier';
export {
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS as STANDARD_OBJECT,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
@@ -1,23 +1,20 @@
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import { type ObjectConfig } from '@/sdk/define/objects/object-config';
import { generateDefaultFieldUniversalIdentifier } from '@/sdk/define/objects/generate-default-field-universal-identifier';
describe('generateDefaultFieldUniversalIdentifier', () => {
it('should generate a unique universal identifier', () => {
const objectConfig = {
universalIdentifier: '55b79f88-4094-4b3f-a0ac-1a91a55714f2',
} as ObjectConfig;
const objectUniversalIdentifier = '55b79f88-4094-4b3f-a0ac-1a91a55714f2';
const uId1 = generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'id',
});
const uId2 = generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'id',
});
const anotherUId = generateDefaultFieldUniversalIdentifier({
objectConfig,
objectUniversalIdentifier,
fieldName: 'name',
});
@@ -1,16 +1,15 @@
import type { ObjectConfig } from '@/sdk/define/objects/object-config';
import { v5 } from 'uuid';
const UNIVERSAL_IDENTIFIER_NAMESPACE = '142046f0-4d80-48b5-ad56-26ad410e895c';
export const generateDefaultFieldUniversalIdentifier = ({
objectConfig,
objectUniversalIdentifier,
fieldName,
}: {
objectConfig: ObjectConfig;
objectUniversalIdentifier: string;
fieldName: string;
}) => {
const seed = `${objectConfig.universalIdentifier}-${fieldName}`;
const seed = `${objectUniversalIdentifier}-${fieldName}`;
return v5(seed, UNIVERSAL_IDENTIFIER_NAMESPACE);
};
@@ -16,6 +16,10 @@ import { GenerateApiKeyCommand } from 'src/engine/core-modules/api-key/commands/
import { MarketplaceModule } from 'src/engine/core-modules/application/application-marketplace/marketplace.module';
import { StaleRegistrationCleanupModule } from 'src/engine/core-modules/application/application-oauth/stale-registration-cleanup/stale-registration-cleanup.module';
import { ApplicationUpgradeModule } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.module';
import { RebuildApplicationDefaultDepsCommand } from 'src/database/commands/rebuild-application-default-deps.command';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { EnforceUsageCapCronCommand } from 'src/engine/core-modules/billing/crons/commands/enforce-usage-cap.cron.command';
import { EnterpriseKeyValidationCronCommand } from 'src/engine/core-modules/enterprise/cron/command/enterprise-key-validation.cron.command';
import { EnterpriseModule } from 'src/engine/core-modules/enterprise/enterprise.module';
@@ -73,6 +77,9 @@ import { AutomatedTriggerModule } from 'src/modules/workflow/workflow-trigger/au
MarketplaceModule,
ApplicationUpgradeModule,
StaleRegistrationCleanupModule,
WorkspaceIteratorModule,
ApplicationModule,
WorkspaceCacheModule,
WorkspaceVersionModule,
UpgradeModule,
],
@@ -88,6 +95,7 @@ import { AutomatedTriggerModule } from 'src/modules/workflow/workflow-trigger/au
GenerateApiKeyCommand,
EnforceUsageCapCronCommand,
UpgradeStatusCommand,
RebuildApplicationDefaultDepsCommand,
],
})
export class DatabaseCommandModule {}
@@ -0,0 +1,94 @@
import chalk from 'chalk';
import { Command, CommandRunner, Option } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { CommandLogger } from 'src/database/commands/logger';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
type RebuildDefaultPackageFilesCommandOptions = {
workspaceId?: Set<string>;
};
@Command({
name: 'application:rebuild-default-deps',
description:
'Re-upload default package.json and yarn.lock to file storage for all applications in the workspace',
})
export class RebuildApplicationDefaultDepsCommand extends CommandRunner {
protected logger: CommandLogger;
constructor(
private readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly applicationService: ApplicationService,
private readonly workspaceCacheService: WorkspaceCacheService,
) {
super();
this.logger = new CommandLogger({
verbose: false,
constructorName: this.constructor.name,
});
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description:
'workspace id. Command runs on all active/suspended workspaces if not provided.',
required: false,
})
parseWorkspaceId(val: string, previous?: Set<string>): Set<string> {
const accumulator = previous ?? new Set<string>();
accumulator.add(val);
return accumulator;
}
override async run(
_passedParams: string[],
options: RebuildDefaultPackageFilesCommandOptions,
): Promise<void> {
const workspaceIds = isDefined(options.workspaceId)
? Array.from(options.workspaceId)
: undefined;
const report = await this.workspaceIteratorService.iterate({
workspaceIds,
callback: async ({ workspaceId }) => {
const { flatApplicationMaps } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatApplicationMaps',
]);
const applications = Object.values(flatApplicationMaps.byId).filter(
(application): application is FlatApplication =>
isDefined(application) && !isDefined(application.deletedAt),
);
this.logger.log(
`Found ${applications.length} application(s) in workspace ${workspaceId}`,
);
for (const application of applications) {
await this.applicationService.uploadDefaultPackageFilesAndSetFileIds({
id: application.id,
universalIdentifier: application.universalIdentifier,
workspaceId,
});
this.logger.log(
`Rebuilt default package files for application "${application.name}" (${application.id})`,
);
}
},
});
if (report.fail.length > 0) {
throw new Error(
`Command completed with ${report.fail.length} failure(s)`,
);
}
this.logger.log(chalk.blue('Command completed!'));
}
}

Some files were not shown because too many files have changed in this diff Show More