i18n - docs translations (#17433)

Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-01-26 07:11:01 +01:00
committed by GitHub
parent c8c821a692
commit 2353bc62cc
361 changed files with 20096 additions and 17317 deletions
@@ -1,147 +1,147 @@
---
title: APIs
description: Query and modify your CRM data programmatically using REST or GraphQL.
description: Consulta y modifica tus datos de CRM de forma programática usando REST o GraphQL.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty was built to be developer-friendly, offering powerful APIs that adapt to your custom data model. We provide four distinct API types to meet different integration needs.
Twenty fue creado para ser amigable con los desarrolladores, ofreciendo APIs potentes que se adaptan a tu modelo de datos personalizado. Proveemos cuatro tipos de API distintos para satisfacer diferentes necesidades de integración.
## Developer-First Approach
## Enfoque centrado en el desarrollador
Twenty generates APIs specifically for your data model:
Twenty genera APIs específicamente para tu modelo de datos:
* **No long IDs required**: Use your object and field names directly in endpoints
* **Standard and custom objects treated equally**: Your custom objects get the same API treatment as built-in ones
* **Dedicated endpoints**: Each object and field gets its own API endpoint
* **Custom documentation**: Generated specifically for your workspace's data model
* **No se requieren IDs largos**: Usa los nombres de tus objetos y campos directamente en los endpoints.
* **Objetos estándar y personalizados tratados por igual**: Tus objetos personalizados reciben el mismo tratamiento de API que los incorporados.
* **Endpoints dedicados**: Cada objeto y campo recibe su propio endpoint de API.
* **Documentación personalizada**: Generada específicamente para el modelo de datos de tu espacio de trabajo.
<Note>
Your personalized API documentation is available under **Settings → API & Webhooks** after creating an API key. Since Twenty generates APIs that match your custom data model, the documentation is unique to your workspace.
Tu documentación personalizada de la API está disponible en **Configuración → API & Webhooks** después de crear una clave de API. Como Twenty genera APIs que coinciden con tu modelo de datos personalizado, la documentación es única para tu espacio de trabajo.
</Note>
## The Two API Types
## Los dos tipos de API
### Core API
### API Principal
Accessed on `/rest/` or `/graphql/`
Accesible en `/rest/` o `/graphql/`
Work with your actual **records** (the data):
Trabaja con tus **registros** reales (los datos):
* Create, read, update, delete People, Companies, Opportunities, etc.
* Query and filter data
* Manage record relationships
* Crear, leer, actualizar y eliminar Personas, Empresas, Oportunidades, etc.
* Consultar y filtrar datos
* Gestionar relaciones de registros
### Metadata API
### API de Metadatos
Accessed on `/rest/metadata/` or `/metadata/`
Accesible en `/rest/metadata/` o `/metadata/`
Manage your **workspace and data model**:
Administra tu **espacio de trabajo y modelo de datos**:
* Create, modify, or delete objects and fields
* Configure workspace settings
* Define relationships between objects
* Crear, modificar o eliminar objetos y campos
* Configurar ajustes del espacio de trabajo
* Define relaciones entre objetos
## REST vs GraphQL
Both Core and Metadata APIs are available in REST and GraphQL formats:
Tanto las API Core como las de Metadatos están disponibles en formatos REST y GraphQL:
| Format | Available Operations |
| ----------- | ---------------------------------------------------------- |
| **REST** | CRUD, batch operations, upserts |
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
| Formato | Operaciones disponibles |
| ----------- | ----------------------------------------------------------------------------- |
| **REST** | CRUD, operaciones por lotes, upserts |
| **GraphQL** | Lo mismo + **upserts por lotes**, consultas de relaciones en una sola llamada |
Choose based on your needs — both formats access the same data.
Elige según tus necesidades — ambos formatos acceden a los mismos datos.
## API Endpoints
## Puntos de Acceso de API
| Environment | Base URL |
| --------------- | ------------------------- |
| **Cloud** | `https://api.twenty.com/` |
| **Self-Hosted** | `https://{your-domain}/` |
| Entorno | URL base |
| ------------------- | ------------------------- |
| **Nube** | `https://api.twenty.com/` |
| **Autoalojamiento** | `https://{your-domain}/` |
## Authentication
## Autenticación
Every API request requires an API key in the header:
Cada solicitud a la API requiere una clave de API en el encabezado:
```
Authorization: Bearer YOUR_API_KEY
```
### Create an API Key
### Crear una Clave de API
1. Go to **Settings → APIs & Webhooks**
2. Click **+ Create key**
3. Configure:
* **Name**: Descriptive name for the key
* **Expiration Date**: When the key expires
4. Click **Save**
5. **Copy immediately** — the key is only shown once
1. Ve a **Configuración → APIs y Webhooks**
2. Haz clic en **+ Crear clave**
3. Configurar:
* **Nombre**: Nombre descriptivo para la clave
* **Fecha de vencimiento**: Cuándo expira la clave
4. Haga clic en **Guardar**
5. **Copia de inmediato** — la clave solo se muestra una vez
<VimeoEmbed videoId="928786722" title="Creating API key" />
<VimeoEmbed videoId="928786722" title="Creación de clave de API" />
<Warning>
Your API key grants access to sensitive data. Don't share it with untrusted services. If compromised, disable it immediately and generate a new one.
Tu clave de API concede acceso a datos sensibles. No la compartas con servicios no confiables. Si se ve comprometida, desactívala de inmediato y genera una nueva.
</Warning>
### Assign a Role to an API Key
### Asignar un rol a una clave de API
For better security, assign a specific role to limit access:
Para mayor seguridad, asigna un rol específico para limitar el acceso:
1. Go to **Settings → Roles**
2. Click on the role to assign
3. Open the **Assignment** tab
4. Under **API Keys**, click **+ Assign to API key**
5. Select the API key
1. Ve a **Configuración → Roles**
2. Haz clic en el rol que deseas asignar
3. Abre la pestaña **Asignación**
4. En **Claves de API**, haz clic en **+ Asignar a clave de API**
5. Selecciona la clave de API
The key will inherit that role's permissions. See [Permissions](/l/es/user-guide/permissions-access/capabilities/permissions) for details.
La clave heredará los permisos de ese rol. Consulta [Permisos](/l/es/user-guide/permissions-access/capabilities/permissions) para más detalles.
### Manage API Keys
### Gestionar Claves de API
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
**Regenerar**: Configuración → APIs & Webhooks → Haz clic en la clave → **Regenerar**
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
**Eliminar**: Configuración → APIs & Webhooks → Haz clic en la clave → **Eliminar**
## API Playground
## Playground de la API
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
Prueba tus API directamente en el navegador con nuestro playground integrado — disponible tanto para **REST** como para **GraphQL**.
### Access the Playground
### Accede al Playground
1. Go to **Settings → APIs & Webhooks**
2. Create an API key (required)
3. Click on **REST API** or **GraphQL API** to open the playground
1. Ve a **Configuración → APIs y Webhooks**
2. Crea una clave de API (obligatorio)
3. Haz clic en **REST API** o **GraphQL API** para abrir el playground
### What You Get
### Lo que obtienes
* **Interactive documentation**: Generated for your specific data model
* **Live testing**: Execute real API calls against your workspace
* **Schema explorer**: Browse available objects, fields, and relationships
* **Request builder**: Construct queries with autocomplete
* **Documentación interactiva**: Generada para tu modelo de datos específico
* **Pruebas en vivo**: Ejecuta llamadas reales a la API en tu espacio de trabajo
* **Explorador de esquemas**: Navega por los objetos, campos y relaciones disponibles
* **Constructor de solicitudes**: Crea consultas con autocompletado
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
El playground refleja tus objetos y campos personalizados, por lo que la documentación siempre es precisa para tu espacio de trabajo.
## Batch Operations
## Operaciones por Lotes
Both REST and GraphQL support batch operations:
Tanto REST como GraphQL admiten operaciones por lotes:
* **Batch size**: Up to 60 records per request
* **Operations**: Create, update, delete multiple records
* **Tamaño del lote**: Hasta 60 registros por solicitud
* **Operaciones**: Crear, actualizar y eliminar múltiples registros
**GraphQL-only features:**
**Características exclusivas de GraphQL:**
* **Batch Upsert**: Create or update in one call
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
* **Upsert por lotes**: Crear o actualizar en una llamada
* Usa nombres de objetos en plural (por ejemplo, `CreateCompanies` en lugar de `CreateCompany`)
## Rate Limits
## Límites de tasa
API requests are throttled to ensure platform stability:
Las solicitudes a la API se limitan para garantizar la estabilidad de la plataforma:
| Limit | Value |
| -------------- | -------------------- |
| **Requests** | 100 calls per minute |
| **Batch size** | 60 records per call |
| Límite | Valor |
| ------------------- | -------------------------- |
| **Solicitudes** | 100 solicitudes por minuto |
| **Tamaño del lote** | 60 registros por llamada |
<Tip>
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
Usa operaciones por lotes para maximizar el rendimiento — procesa hasta 60 registros en una sola llamada a la API en lugar de hacer solicitudes individuales.
</Tip>
@@ -1,81 +1,88 @@
---
title: Twenty Apps
description: Build and manage Twenty customizations as code.
title: Aplicaciones de Twenty
description: Crea y gestiona personalizaciones de Twenty como código.
---
<Warning>
Apps are currently in alpha testing. The feature is functional but still evolving.
Las aplicaciones están actualmente en pruebas alfa. La funcionalidad es operativa, pero sigue evolucionando.
</Warning>
## What Are Apps?
## ¿Qué son las aplicaciones?
Apps let you build and manage Twenty customizations **as code**. Instead of configuring everything through the UI, you define your data model and serverless functions in code — making it faster to build, maintain, and roll out to multiple workspaces.
Las aplicaciones te permiten crear y administrar personalizaciones de Twenty **como código**. En lugar de configurar todo a través de la interfaz de usuario, defines tu modelo de datos y funciones sin servidor en código, lo que hace más rápido crear, mantener y desplegar en múltiples espacios de trabajo.
**What you can do today:**
**Lo que puedes hacer hoy:**
* Define custom objects and fields as code (managed data model)
* Build serverless functions with custom triggers
* Deploy the same app across multiple workspaces
* Define objetos y campos personalizados como código (modelo de datos gestionado)
* Crea funciones sin servidor con desencadenadores personalizados
* Despliega la misma aplicación en múltiples espacios de trabajo
**Coming soon:**
**Próximamente:**
* Custom UI layouts and components
* Diseños y componentes de la interfaz de usuario personalizados
## Prerequisites
## Prerrequisitos
* Node.js 24+ and Yarn 4
* A Twenty workspace and an API key (create one at https://app.twenty.com/settings/api-webhooks)
* Node.js 24+ y Yarn 4
* Un espacio de trabajo de Twenty y una clave de API (créala en https://app.twenty.com/settings/api-webhooks)
## Getting Started
## Primeros pasos
Create a new app using the official scaffolder, then authenticate and start developing:
Crea una aplicación nueva usando el generador oficial, luego autentícate y comienza a desarrollar:
```bash filename="Terminal"
# Scaffold a new app
# Crear la estructura de una nueva aplicación
npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
# Authenticate using your API key (you'll be prompted)
yarn auth
# Si no usas yarn@4
corepack enable
yarn install
# Start dev mode: automatically syncs local changes to your workspace
yarn dev
# Autentícate con tu clave de API (se te pedirá)
yarn auth:login
# Inicia el modo de desarrollo: sincroniza automáticamente los cambios locales con tu espacio de trabajo
yarn app:dev
```
From here you can:
Desde aquí usted puede:
```bash filename="Terminal"
# Add a new entity to your application (guided)
yarn create-entity
yarn app:create-entity
# Generate a typed Twenty client and workspace entity types
yarn generate
yarn app:generate
# Run a onetime sync (instead of watch mode)
yarn sync
yarn app:sync
# Watch your application's functions logs
yarn logs
yarn function:logs
# Execute a function by name
yarn function:execute -n my-function -p '{"name": "test"}'
# Uninstall the application from the current workspace
yarn uninstall
yarn app:uninstall
# Display commands' help
yarn help
yarn app:help
```
See also: the CLI reference pages for [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) and [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
Consulta también: las páginas de referencia de la CLI para [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) y [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
## Project structure (scaffolded)
## Estructura del proyecto (generada)
When you run `npx create-twenty-app@latest my-twenty-app`, the scaffolder:
Cuando ejecutas `npx create-twenty-app@latest my-twenty-app`, el generador:
* Copies a minimal base application into `my-twenty-app/`
* Adds a local `twenty-sdk` dependency and Yarn 4 configuration
* Creates config files and scripts wired to the `twenty` CLI
* Generates a default application config and a default function role
* Copia una aplicación base mínima en `my-twenty-app/`
* Añade una dependencia local de `twenty-sdk` y la configuración de Yarn 4
* Crea archivos de configuración y scripts vinculados a la CLI `twenty`
* Genera una configuración de aplicación predeterminada y un rol de función predeterminado
A freshly scaffolded app looks like this:
Una aplicación recién generada se ve así:
```text filename="my-twenty-app/"
my-twenty-app/
@@ -85,79 +92,144 @@ my-twenty-app/
.nvmrc
.yarnrc.yml
.yarn/
releases/
yarn-4.9.2.cjs
install-state.gz
eslint.config.mjs
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Obligatorio - configuración principal de la aplicación
default-function.role.ts # Rol predeterminado para las funciones sin servidor
// tus entidades (*.object.ts, *.function.ts, *.role.ts)
utils/ # Opcional - implementaciones de manejadores y utilidades
```
At a high level:
### Convención sobre configuración
* **package.json**: Declares the app name, version, engines (Node 24+, Yarn 4), and adds `twenty-sdk` plus scripts like `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall`, and `auth` that delegate to the local `twenty` CLI.
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See “Application config” below.
* `role.config.ts`: Default function role used by your serverless functions. See “Default function role” below.
* Future entities, actions/functions, and any supporting code you add.
Las aplicaciones usan un enfoque de **convención sobre configuración** en el que las entidades se detectan por su sufijo de archivo. Esto permite una organización flexible dentro de la carpeta `src/app/`:
Later commands will add more files and folders:
| Sufijo de archivo | Tipo de entidad |
| ----------------- | -------------------------------------- |
| `*.object.ts` | Definiciones de objetos personalizados |
| `*.function.ts` | Definiciones de funciones sin servidor |
| `*.role.ts` | Definiciones de roles |
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
### Organizaciones de carpetas compatibles
## Authentication
Puedes organizar tus entidades con cualquiera de estos patrones:
The first time you run `yarn auth`, you'll be prompted for:
**Tradicional (por tipo):**
* API URL (defaults to http://localhost:3000 or your current workspace profile)
* API key
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch using `--workspace <name>`.
**Basado en funcionalidades:**
Examples:
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**Plano:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
A grandes rasgos:
* **package.json**: Declara el nombre de la aplicación, la versión, los entornos (Node 24+, Yarn 4) y agrega `twenty-sdk` además de scripts como `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall` y `auth` que delegan en la CLI local `twenty`.
* **.gitignore**: Ignora artefactos comunes como `node_modules`, `.yarn`, `generated/` (cliente tipado), `dist/`, `build/`, carpetas de cobertura, archivos de registro y archivos `.env*`.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Bloquean y configuran la cadena de herramientas Yarn 4 utilizada por el proyecto.
* **.nvmrc**: Fija la versión de Node.js esperada por el proyecto.
* **eslint.config.mjs** y **tsconfig.json**: Proporcionan linting y configuración de TypeScript para las fuentes de TypeScript de tu aplicación.
* **README.md**: Un README breve en la raíz de la aplicación con instrucciones básicas.
* **src/app/**: El lugar principal donde defines tu aplicación como código:
* `application.config.ts`: Configuración global de tu aplicación (metadatos y vinculación en tiempo de ejecución). Consulta "Configuración de la aplicación" más abajo.
* `*.role.ts`: Definiciones de roles usadas por tus funciones sin servidor. Consulta "Rol de función predeterminado" más abajo.
* `*.object.ts`: Definiciones de objetos personalizados.
* `*.function.ts`: Definiciones de funciones sin servidor.
* **src/utils/**: Carpeta opcional para implementaciones de controladores y utilidades.
Comandos posteriores añadirán más archivos y carpetas:
* `yarn app:generate` creará una carpeta `generated/` (cliente tipado de Twenty + tipos del espacio de trabajo).
* `yarn app:create-entity` añadirá archivos de definición de entidades en `src/app/` para tus objetos, funciones o roles personalizados.
l
## Autenticación
La primera vez que ejecutes `yarn auth:login`, se te solicitará:
* URL de la API (por defecto http://localhost:3000 o el perfil de tu espacio de trabajo actual)
* Clave de API
Tus credenciales se almacenan por usuario en `~/.twenty/config.json`. Puedes mantener varios perfiles y cambiar entre ellos.
### Gestión de espacios de trabajo
```bash filename="Terminal"
# Login interactively (recommended)
yarn auth
yarn auth:login
# Use a specific workspace profile
yarn auth --workspace my-custom-workspace
# Login to a specific workspace profile
yarn auth:login --workspace my-custom-workspace
# List all configured workspaces
yarn auth:list
# Switch the default workspace (interactive)
yarn auth:switch
# Switch to a specific workspace
yarn auth:switch production
# Check current authentication status
yarn auth:status
```
## Use the SDK resources (types & config)
Una vez que hayas cambiado de espacio de trabajo con `auth:switch`, todos los comandos posteriores usarán ese espacio de trabajo de forma predeterminada. Aún puedes anularlo temporalmente con `--workspace <name>`.
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
## Usa los recursos del SDK (tipos y configuración)
### Defining objects
El twenty-sdk proporciona bloques de construcción tipados y funciones auxiliares que utilizas dentro de tu aplicación. A continuación, las partes clave que usarás con más frecuencia.
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
### Funciones auxiliares
Here is an example `postCard` object from the Hello World app:
El SDK proporciona cuatro funciones auxiliares con validación incorporada para definir las entidades de tu aplicación:
| Función | Propósito |
| ------------------ | ----------------------------------------------- |
| `defineApp()` | Configura los metadatos de la aplicación |
| `defineObject()` | Define objetos personalizados con campos |
| `defineFunction()` | Define funciones sin servidor con controladores |
| `defineRole()` | Configura permisos de roles y acceso a objetos |
Estas funciones validan tu configuración en tiempo de ejecución y proporcionan un mejor autocompletado en el IDE y seguridad de tipos.
### Definir objetos
Los objetos personalizados describen tanto el esquema como el comportamiento de los registros en tu espacio de trabajo. Usa `defineObject()` para definir objetos con validación incorporada:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,176 +238,186 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
Key points:
Puntos clave:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* Usa `defineObject()` para validación incorporada y mejor soporte del IDE.
* El `universalIdentifier` debe ser único y estable entre implementaciones.
* Cada campo requiere `name`, `type`, `label` y su propio `universalIdentifier` estable.
* La matriz `fields` es opcional: puedes definir objetos sin campos personalizados.
* Puedes generar nuevos objetos usando `yarn app:create-entity`, que te guía por el nombrado, los campos y las relaciones.
### Application config (application.config.ts)
<Note>
**Los campos base se crean automáticamente.** Cuando defines un objeto personalizado, Twenty añade automáticamente campos estándar como `name`, `createdAt`, `updatedAt`, `createdBy`, `position` y `deletedAt`. No necesitas definir estos en tu matriz `fields` — solo agrega tus campos personalizados.
</Note>
Every app has a single `application.config.ts` file that describes:
<Accordion title="Alternativa: sintaxis basada en decoradores">
También puedes definir objetos usando decoradores de TypeScript. Este enfoque usa sintaxis basada en clases con los decoradores `@Object`, `@Field` y `@Relation`:
* **Who the app is**: identifiers, display name, and description.
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
When you scaffold a new app, you start with a minimal config:
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
Nota: El enfoque de decoradores requiere `experimentalDecorators` en tu configuración de TypeScript.
</Accordion>
### Configuración de la aplicación (application.config.ts)
Cada aplicación tiene un único archivo `application.config.ts` que describe:
* **Qué es la aplicación**: identificadores, nombre para mostrar y descripción.
* **Cómo se ejecutan sus funciones**: qué rol usan para permisos.
* **Variables (opcionales)**: pares clavevalor expuestos a tus funciones como variables de entorno.
Usa `defineApp()` para definir la configuración de tu aplicación:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
Notas:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* Los campos `universalIdentifier` son ID deterministas bajo tu control; genéralos una vez y mantenlos estables entre sincronizaciones.
* Las `applicationVariables` se convierten en variables de entorno para tus funciones (por ejemplo, `DEFAULT_RECIPIENT_NAME` está disponible como `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` debe coincidir con el rol que defines en tu archivo `*.role.ts` (ver abajo).
#### Roles and permissions
#### Roles y permisos
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
Las aplicaciones pueden definir roles que encapsulan permisos sobre los objetos y acciones de tu espacio de trabajo. El campo `functionRoleUniversalIdentifier` en `application.config.ts` designa el rol predeterminado que usan las funciones sin servidor de tu aplicación.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
* La clave de API en tiempo de ejecución inyectada como `TWENTY_API_KEY` se deriva de este rol de función predeterminado.
* El cliente tipado estará restringido a los permisos otorgados a ese rol.
* Sigue el principio de mínimo privilegio: crea un rol dedicado con solo los permisos que necesitan tus funciones y luego referencia su identificador universal.
##### Default function role (role.config.ts)
##### Rol de función predeterminado (\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
Cuando generas una nueva aplicación, la CLI también crea un archivo de rol predeterminado. Usa `defineRole()` para definir roles con validación incorporada:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,41 +445,41 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
Notes:
El `universalIdentifier` de este rol se referencia luego en `application.config.ts` como `functionRoleUniversalIdentifier`. En otras palabras:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
* Replace the `objectPermissions` and `fieldPermissions` with the objects/fields your functions need.
* `permissionFlags` control access to platform-level capabilities. Keep them minimal; add only what you need.
* See a working example in the Hello World app: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
* **\*.role.ts** define lo que puede hacer el rol de función predeterminado.
* **application.config.ts** apunta a ese rol para que tus funciones hereden sus permisos.
### Serverless function config and entrypoint
Notas:
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
* Parte del rol generado y luego restríngele progresivamente siguiendo el principio de mínimo privilegio.
* Reemplaza `objectPermissions` y `fieldPermissions` con los objetos/campos que necesitan tus funciones.
* `permissionFlags` controla el acceso a capacidades a nivel de plataforma. Mantenlos al mínimo; agrega solo lo que necesites.
* Consulta un ejemplo funcional en la aplicación Hello World: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
### Configuración y punto de entrada de funciones sin servidor
Cada archivo de función usa `defineFunction()` para exportar una configuración con un controlador y desencadenadores opcionales. Usa el sufijo de archivo `*.function.ts` para la detección automática.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| RoutePayload
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
| CronPayload,
) => {
const client = new Twenty(); // generated typed client
const name = 'name' in params
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
const name = 'name' in params.queryStringParameters
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
: 'Hello world';
const result = await client.mutation({
@@ -410,14 +492,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,39 +508,137 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
eventName: 'person.updated',
updatedFields: ['name'],
},
],
});
```
Tipos de desencadenadores comunes:
* **route**: Expone tu función en una ruta y método HTTP **bajo el endpoint `/s/`**:
> p. ej. `path: '/post-card/create',` -> llamar en `<APP_URL>/s/post-card/create`
* **cron**: Ejecuta tu función en un horario usando una expresión CRON.
* **databaseEvent**: Se ejecuta en eventos del ciclo de vida de objetos del espacio de trabajo. Cuando la operación del evento es `updated`, se pueden especificar campos específicos que se deben escuchar en el arreglo `updatedFields`. Si se deja sin definir o vacío, cualquier actualización activará la función.
> p. ej., `person.updated`
Notas:
* La matriz `triggers` es opcional. Las funciones sin desencadenadores pueden usarse como funciones utilitarias llamadas por otras funciones.
* Puedes combinar múltiples tipos de desencadenadores en una sola función.
### Carga útil del disparador de ruta
<Warning>
**Cambio no retrocompatible (v1.16, enero de 2026):** El formato de la carga útil del disparador de ruta ha cambiado. Antes de la v1.16, los parámetros de consulta, los parámetros de ruta y el cuerpo se enviaban directamente como la carga útil. A partir de la v1.16, están anidados dentro de un objeto `RoutePayload` estructurado.
**Antes de la v1.16:**
```typescript
const handler = async (params) => {
const { param1, param2 } = params; // Direct access
};
```
**Después de la v1.16:**
```typescript
const handler = async (event: RoutePayload) => {
const { param1, param2 } = event.body; // Access via .body
const { queryParam } = event.queryStringParameters;
const { id } = event.pathParameters;
};
```
**Para migrar las funciones existentes:** Actualiza tu controlador para desestructurar desde `event.body`, `event.queryStringParameters` o `event.pathParameters` en lugar de hacerlo directamente desde el objeto params.
</Warning>
Cuando un disparador de ruta invoca tu función, esta recibe un objeto `RoutePayload` que sigue el formato de AWS HTTP API v2. Importa el tipo desde `twenty-sdk`:
```typescript
import { defineFunction, type RoutePayload } from 'twenty-sdk';
const handler = async (event: RoutePayload) => {
// Access request data
const { headers, queryStringParameters, pathParameters, body } = event;
// HTTP method and path are available in requestContext
const { method, path } = event.requestContext.http;
return { message: 'Success' };
};
```
Common trigger types:
El tipo `RoutePayload` tiene la siguiente estructura:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
| Propiedad | Tipo | Descripción |
| ---------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- |
| `headers` | `Record<string, string \| undefined>` | Encabezados HTTP (solo aquellos listados en `forwardedRequestHeaders`) |
| `queryStringParameters` | `Record<string, string \| undefined>` | Parámetros de consulta (valores múltiples unidos con comas) |
| `pathParameters` | `Record<string, string \| undefined>` | Parámetros de ruta extraídos del patrón de ruta (p. ej., `/users/:id` → `{ id: '123' }`) |
| `cuerpo` | `object \| null` | Cuerpo de la solicitud analizado (JSON) |
| `isBase64Encoded` | `booleano` | Indica si el cuerpo está codificado en base64 |
| `requestContext.http.method` | `string` | Método HTTP (GET, POST, PUT, PATCH, DELETE) |
| `requestContext.http.path` | `string` | Ruta de la solicitud sin procesar |
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
### Reenvío de encabezados HTTP
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
De forma predeterminada, los encabezados HTTP de las solicitudes entrantes **no** se pasan a tu función sin servidor por razones de seguridad. Para acceder a encabezados específicos, enuméralos explícitamente en el arreglo `forwardedRequestHeaders`:
> e.g. `person.created`
```typescript
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'webhook-handler',
handler,
triggers: [
{
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/webhook',
httpMethod: 'POST',
isAuthRequired: false,
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
},
],
});
```
You can create new functions in two ways:
En tu controlador, luego puedes acceder a estos encabezados:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
```typescript
const handler = async (event: RoutePayload) => {
const signature = event.headers['x-webhook-signature'];
const contentType = event.headers['content-type'];
### Generated typed client
// Validate webhook signature...
return { received: true };
};
```
Run yarn generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
<Note>
Los nombres de los encabezados se normalizan a minúsculas. Accede a ellos usando claves en minúsculas (por ejemplo, `event.headers['content-type']`).
</Note>
Puedes crear funciones nuevas de dos maneras:
* **Generado**: Ejecuta `yarn app:create-entity` y elige la opción para añadir una nueva función. Esto genera un archivo inicial con un controlador y configuración.
* **Manual**: Crea un nuevo archivo `*.function.ts` y usa `defineFunction()`, siguiendo el mismo patrón.
### Cliente tipado generado
Ejecuta yarn app:generate para crear un cliente tipado local en generated/ basado en el esquema de tu espacio de trabajo. Úsalo en tus funciones:
```typescript
import Twenty from './generated';
@@ -466,34 +647,34 @@ const client = new Twenty();
const { me } = await client.query({ me: { id: true, displayName: true } });
```
The client is re-generated by `yarn generate`. Re-run after changing your objects and `yarn sync` or when onboarding to a new workspace.
El cliente se vuelve a generar con `yarn app:generate`. Vuelve a ejecutarlo después de cambiar tus objetos y de hacer `yarn app:sync`, o al incorporarte a un nuevo espacio de trabajo.
#### Runtime credentials in serverless functions
#### Credenciales en tiempo de ejecución en funciones sin servidor
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
Cuando tu función se ejecuta en Twenty, la plataforma inyecta credenciales como variables de entorno antes de que tu código se ejecute:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_URL`: URL base de la API de Twenty a la que apunta tu aplicación.
* `TWENTY_API_KEY`: Clave de corta duración con alcance al rol de función predeterminado de tu aplicación.
Notes:
Notas:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* No necesitas pasar la URL ni la clave de API al cliente generado. Lee `TWENTY_API_URL` y `TWENTY_API_KEY` de process.env en tiempo de ejecución.
* Los permisos de la clave de API están determinados por el rol referenciado en tu `application.config.ts` mediante `functionRoleUniversalIdentifier`. Este es el rol predeterminado que usan las funciones sin servidor de tu aplicación.
* Las aplicaciones pueden definir roles para seguir el principio de mínimo privilegio. Concede solo los permisos que necesitan tus funciones y después apunta `functionRoleUniversalIdentifier` al identificador universal de ese rol.
### Hello World example
### Ejemplo Hello World
Explore a minimal, end-to-end example that demonstrates objects, functions, and multiple triggers [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
Explora un ejemplo mínimo de extremo a extremo que demuestra objetos, funciones y múltiples desencadenadores [aquí](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
## Manual setup (without the scaffolder)
## Configuración manual (sin el generador)
While we recommend using `create-twenty-app` for the best getting-started experience, you can also set up a project manually. Do not install the CLI globally. Instead, add `twenty-sdk` as a local dependency and wire scripts in your package.json:
Aunque recomendamos usar `create-twenty-app` para la mejor experiencia de inicio, también puedes configurar un proyecto manualmente. No instales la CLI globalmente. En su lugar, agrega `twenty-sdk` como dependencia local y conecta scripts en tu package.json:
```bash filename="Terminal"
yarn add -D twenty-sdk
```
Then add scripts like these:
Luego agrega scripts como estos:
```json filename="package.json"
{
@@ -510,13 +691,13 @@ Then add scripts like these:
}
```
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
Ahora puedes ejecutar los mismos comandos mediante Yarn, p. ej., `yarn app:dev`, `yarn app:sync`, etc.
## Troubleshooting
## Solución de problemas
* Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
* Cannot connect to server: verify the API URL and that the Twenty server is reachable.
* Types or client missing/outdated: run `yarn generate` and then `yarn dev`.
* Dev mode not syncing: ensure `yarn dev` is running and that changes are not ignored by your environment.
* Errores de autenticación: ejecuta `yarn auth:login` y asegúrate de que tu clave de API tenga los permisos necesarios.
* No se puede conectar al servidor: verifica la URL de la API y que el servidor de Twenty sea accesible.
* Tipos o cliente faltantes/obsoletos: ejecuta `yarn app:generate` y luego `yarn app:dev`.
* El modo de desarrollo no sincroniza: asegúrate de que `yarn app:dev` esté ejecutándose y de que los cambios no sean ignorados por tu entorno.
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
Canal de ayuda en Discord: https://discord.com/channels/1130383047699738754/1130386664812982322
@@ -1,44 +1,44 @@
---
title: Webhooks
description: Receive real-time notifications when events occur in your CRM.
description: Recibe notificaciones en tiempo real cuando ocurran eventos en tu CRM.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Webhooks push data to your systems in real-time when events occur in Twenty — no polling required. Use them to keep external systems in sync, trigger automations, or send alerts.
Los webhooks envían datos a tus sistemas en tiempo real cuando ocurren eventos en Twenty — no se requiere sondeo. Úsalos para mantener sincronizados los sistemas externos, activar automatizaciones o enviar alertas.
## Create a Webhook
## Crear un Webhook
1. Go to **Settings → APIs & Webhooks → Webhooks**
2. Click **+ Create webhook**
3. Enter your webhook URL (must be publicly accessible)
4. Click **Save**
1. Ve a **Configuración → APIs y Webhooks → Webhooks**
2. Haga clic en **+ Crear webhook**
3. Introduce la URL de tu webhook (debe ser públicamente accesible)
4. Haga clic en **Guardar**
The webhook activates immediately and starts sending notifications.
El webhook se activa de inmediato y comienza a enviar notificaciones.
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
<VimeoEmbed videoId="928786708" title="Crear un webhook" />
### Manage Webhooks
### Gestionar Webhooks
**Edit**: Click the webhook → Update URL → **Save**
**Editar**: Haz clic en el webhook → Actualizar la URL → **Guardar**
**Delete**: Click the webhook → **Delete** → Confirm
**Eliminar**: Haz clic en el webhook → **Eliminar** → Confirmar
## Events
## Eventos
Twenty sends webhooks for these event types:
Twenty envía webhooks para estos tipos de eventos:
| Event | Example |
| ------------------ | ---------------------------------------------------------- |
| **Record Created** | `person.created`, `company.created`, `note.created` |
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
| **Record Deleted** | `person.deleted`, `company.deleted` |
| Evento | Ejemplo |
| ---------------------------- | ---------------------------------------------------------- |
| **Se crea un registro** | `person.created`, `company.created`, `note.created` |
| **Se actualiza un registro** | `person.updated`, `company.updated`, `opportunity.updated` |
| **Se elimina un registro** | `person.deleted`, `company.deleted` |
All event types are sent to your webhook URL. Event filtering may be added in future releases.
Todos los tipos de eventos se envían a la URL de tu webhook. Es posible que se agregue el filtrado de eventos en versiones futuras.
## Payload Format
## Formato de la carga útil
Each webhook sends an HTTP POST with a JSON body:
Cada webhook envía una solicitud HTTP POST con un cuerpo JSON:
```json
{
@@ -55,35 +55,35 @@ Each webhook sends an HTTP POST with a JSON body:
}
```
| Field | Description |
| ----------- | ------------------------------------------------ |
| `event` | What happened (e.g., `person.created`) |
| `data` | The full record that was created/updated/deleted |
| `timestamp` | When the event occurred (UTC) |
| Campo | Descripción |
| ----------------- | -------------------------------------------------- |
| `evento` | Qué ocurrió (p. ej., `person.created`) |
| `datos` | El registro completo que se creó/actualizó/eliminó |
| `marca de tiempo` | Cuándo ocurrió el evento (UTC) |
<Note>
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
Responde con un **estado HTTP 2xx** (200-299) para confirmar la recepción. Las respuestas que no sean 2xx se registran como errores de entrega.
</Note>
## Webhook Validation
## Validación de Webhook
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
Twenty firma cada solicitud de webhook por seguridad. Valida las firmas para garantizar que las solicitudes sean auténticas.
### Headers
### Encabezados
| Header | Description |
| ---------------------------- | --------------------- |
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
| Encabezado | Descripción |
| ---------------------------- | ------------------------------- |
| `X-Twenty-Webhook-Signature` | Firma HMAC SHA256 |
| `X-Twenty-Webhook-Timestamp` | Marca de tiempo de la solicitud |
### Validation Steps
### Pasos de validación
1. Get the timestamp from `X-Twenty-Webhook-Timestamp`
2. Create the string: `{timestamp}:{JSON payload}`
3. Compute HMAC SHA256 using your webhook secret
4. Compare with `X-Twenty-Webhook-Signature`
1. Obtén la marca de tiempo de `X-Twenty-Webhook-Timestamp`
2. Crea la cadena: `{timestamp}:{JSON payload}`
3. Calcula HMAC SHA256 usando tu secreto de webhook
4. Compara con `X-Twenty-Webhook-Signature`
### Example (Node.js)
### Ejemplo (Node.js)
```javascript
const crypto = require("crypto");
@@ -101,12 +101,12 @@ const expectedSignature = crypto
const isValid = expectedSignature === req.headers["x-twenty-webhook-signature"];
```
## Webhooks vs Workflows
## Webhooks vs flujos de trabajo
| Method | Direction | Use Case |
| ---------------------------- | --------- | ---------------------------------------------------------- |
| **Webhooks** | OUT | Automatically notify external systems of any record change |
| **Workflow + HTTP Request** | OUT | Send data out with custom logic (filters, transformations) |
| **Workflow Webhook Trigger** | IN | Receive data into Twenty from external systems |
| Método | Dirección | Caso de uso |
| --------------------------------------------- | --------- | --------------------------------------------------------------------------------- |
| **Webhooks** | SALIDA | Notificar automáticamente a los sistemas externos cualquier cambio en un registro |
| **Flujo de trabajo + solicitud HTTP** | SALIDA | Enviar datos con lógica personalizada (filtros, transformaciones) |
| **Disparador de webhook de flujo de trabajo** | ENTRADA | Recibir datos en Twenty desde sistemas externos |
For receiving external data, see [Set Up a Webhook Trigger](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
Para recibir datos externos, consulta [Configurar un disparador de webhook](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).