> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Personalizar mensajes con custom_data de la API

> Envía datos dinámicos y específicos por mensaje a través de la API Create Message usando custom_data. Haz referencia a valores en plantillas con sintaxis Liquid para personalización en tiempo real.

Usa el campo `custom_data` en la [API Create Message](/reference/create-message) para enviar datos dinámicos desde tu backend y renderizarlos dentro de plantillas usando [sintaxis Liquid](./using-liquid-syntax).

`custom_data`:

* Es **específico del mensaje**
* **No se almacena**
* Existe solo durante la solicitud de la API
* Debe usarse con un `template_id`

Haz referencia a valores en plantillas con:

```liquid Liquid theme={null}
{{ message.custom_data.key_name }}
```

<Warning>
  `custom_data` es efímero. Los datos no se guardan en los perfiles de usuario y no se pueden reutilizar en mensajes futuros. Si necesitas datos persistentes, consulta [Personalización de mensajes](./message-personalization).
</Warning>

***

## Cuándo usar `custom_data`

Usa `custom_data` cuando necesites:

* Datos que cambian por mensaje (totales de pedidos, artículos del carrito, saldos)
* Necesites arrays (listas de productos, líneas de artículos, recomendaciones)
* Los datos no deben persistir (códigos de un solo uso, URLs temporales)
* Envías mensajes activados desde el backend
* Quieres personalización masiva en una sola solicitud de API

***

## Cómo funciona la personalización con `custom_data`

Agregar `custom_data` a los mensajes requiere algunos pasos:

<Steps>
  <Step title="Crear una plantilla">
    Crea una plantilla de Push, Email o SMS en [Templates](./templates) desde el panel o a través de la [API Create Template](/reference/create-template).
  </Step>

  <Step title="Agregar marcadores de posición Liquid">
    Inserta referencias usando el prefijo requerido:

    ```liquid Liquid theme={null}
    Hi {{ message.custom_data.first_name }},
    Order {{ message.custom_data.order_id }} is confirmed.
    ```
  </Step>

  <Step title="Enviar custom_data en tu solicitud de API">
    Llama a la [API Create Message](/reference/create-message) con:

    * `template_id` - El ID de la plantilla
    * `custom_data` - El objeto de datos
    * Segmentación de audiencia (`include_player_ids`, `include_aliases` o segmentos)

    OneSignal renderiza la plantilla en el momento del envío usando tus datos.

    Si la sintaxis Liquid es inválida o las claves no existen, esos campos se renderizan como cadenas vacías, pero el mensaje se envía de todas formas.
  </Step>
</Steps>

***

## Patrones de datos

Ejemplos comunes de patrones de datos que puedes usar con `custom_data`.

### Ejemplo de JSON plano

Usa pares clave-valor simples para personalización básica como nombres, IDs, URLs o cualquier dato de valor único.

**Caso de uso:** Mensajes transaccionales (facturas, recibos, confirmaciones) donde cada campo contiene un solo valor.

**Plantilla:**

```liquid Liquid theme={null}
Invoice {{ message.custom_data.invoice_id }} for {{ message.custom_data.product_name }} is ready.
```

**Solicitud de API:**

```json JSON theme={null}
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_email_tokens": ["user@example.com"],
  "custom_data": {
    "invoice_id": "463246732",
    "product_name": "Widget"
  }
}
```

**Lo que ve el cliente:**

```liquid Text theme={null}
Invoice 463246732 for Widget is ready.
```

***

### Ejemplo de datos con arrays

Pasa arrays de objetos para trabajar con múltiples elementos como productos del carrito, líneas de artículos de un pedido o recomendaciones. Los arrays permiten tanto el acceso directo (indexación) como la iteración (bucles).

**Caso de uso:** Mostrar listas de productos, tablas de clasificación, resúmenes de pedidos o cualquier dato con múltiples elementos.

**Plantilla con indexación (accediendo al primer elemento):**

```liquid Liquid theme={null}
Your {{message.custom_data.cart_items[0].item_name}} is waiting for you!
Image: {{message.custom_data.cart_items[0].img_url}}
```

<Warning>
  **La indexación de arrays comienza en 0**, no en 1. El primer elemento es `[0]`, el segundo es `[1]`, etc. Acceder a un índice que no existe devuelve vacío (no se genera un error).
</Warning>

**Plantilla con bucle (accediendo a todos los elementos):**

```liquid Liquid theme={null}
{% for item in message.custom_data.cart_items %}
- {{ item.item_name }} — {{ item.img_url }}
{% endfor %}
```

**Solicitud de API:**

```json theme={null}
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_email_tokens": ["user@example.com"],
  "custom_data": {
    "cart_items": [
      {
        "item_name": "sweater",
        "img_url": "https://.../sweater.png"
      },
      {
        "item_name": "socks",
        "img_url": "https://.../socks.png"
      }
    ]
  }
}
```

**Lo que ve el cliente:**

```liquid Text theme={null}
Your sweater is waiting for you!
Image: https://.../sweater.png

- sweater — https://.../sweater.png
- socks — https://.../socks.png
```

**Propiedades útiles de arrays:**

* `{{message.custom_data.cart_items.size}}` — Número de elementos en el array (devuelve `2` en este ejemplo)
* `{{message.custom_data.cart_items.first.item_name}}` — Nombre del primer elemento (equivalente a `[0]`)
* `{{message.custom_data.cart_items.last.item_name}}` — Nombre del último elemento

***

### Ejemplo de personalización masiva

Envía una sola solicitud de API a múltiples usuarios donde cada destinatario ve contenido personalizado basado en su `external_id`.

**Cómo funciona:**

1. Estructura `custom_data` como un objeto donde **las claves son external\_ids** y **los valores son datos específicos del usuario**
2. En la plantilla, usa `subscription.external_id` para buscar los datos del destinatario actual
3. OneSignal renderiza la plantilla una vez por destinatario con sus datos específicos

**Plantilla:**

```liquid theme={null}
{% assign user = message.custom_data.users[subscription.external_id] %}
Hi {{ user.first_name }}, you have {{ user.points }} points. Your level is {{ user.level }}.
```

**Qué está ocurriendo:**

* `subscription.external_id` contiene el external\_id del destinatario actual (por ejemplo, "user123")
* `message.custom_data.users[subscription.external_id]` busca los datos de ese usuario en el objeto custom\_data
* `user` se convierte en una variable abreviada para los datos de ese usuario
* Cada destinatario solo ve su propio contenido personalizado

**Solicitud de API:**

```json theme={null}
{
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_aliases": {
    "external_id": ["user123", "user456"]
  },
  "custom_data": {
    "users": {
      "user123": { "first_name": "John", "points": "150", "level": "Gold" },
      "user456": { "first_name": "Sarah", "points": "200", "level": "Platinum" }
    }
  }
}
```

**Lo que ve cada usuario:**

<Check>
  * **John** (user123): "Hi John, you have 150 points. Your level is Gold."
  * **Sarah** (user456): "Hi Sarah, you have 200 points. Your level is Platinum."
</Check>

<Info>
  **Requisitos para la personalización masiva:**

  * Todos los destinatarios deben tener un `external_id` configurado en OneSignal
  * Cada `external_id` en `include_aliases` debe tener una clave correspondiente en `custom_data.users`
  * Si el external\_id de un destinatario no está presente en `custom_data`, su mensaje tendrá campos vacíos
</Info>

***

## Ejemplo: Carrito abandonado con custom\_data

Cómo crear mensajes de carrito abandonado tanto para email como para push usando `custom_data`.

**Cuándo usar este enfoque:**

* Tu servidor detecta el abandono del carrito (por ejemplo, 1 hora después de la última actividad)
* Los datos del carrito en tiempo real están en tu base de datos
* Quieres mostrar múltiples productos con imágenes, nombres y precios
* Cada usuario puede tener diferentes artículos y cantidades
* Quieres orquestar el mensaje desde tu backend

### Ejemplo de payload `custom_data`

Esta es la solicitud de la [API Create Message](/reference/create-message) para este ejemplo.

```json JSON theme={null}
{
  "custom_data": {
    "cart_url": "https://yourdomain.com/cart",
    "cart": [
      {
        "product_name": "24 Pack of Acorns",
        "product_image": "https://i.imgur.com/ssPCfbC.png",
        "product_price": "$12.99",
        "product_quantity": "1"
      },
      {
        "product_name": "Fancy Sweater",
        "product_image": "https://i.imgur.com/8QWTfV4.png",
        "product_price": "$9.99",
        "product_quantity": "1"
      }
    ]
  },
  "app_id": "YOUR_APP_ID",
  "template_id": "YOUR_TEMPLATE_ID",
  "include_aliases": {
    "external_id": ["YOUR_EXTERNAL_ID"]
  }
}
```

**Explicación de los campos:**

| Campo              | Tipo   | Propósito                                                               |
| ------------------ | ------ | ----------------------------------------------------------------------- |
| `cart_url`         | string | Enlace único del carrito del cliente (para botones/URLs de lanzamiento) |
| `cart`             | array  | Lista de productos: admite conteo, bucles y visualización de detalles   |
| `product_image`    | string | Imagen del producto (por elemento en el array)                          |
| `product_name`     | string | Nombre del producto (por elemento)                                      |
| `product_quantity` | string | Cantidad (por elemento)                                                 |
| `product_price`    | string | Precio con formato (por elemento)                                       |

<Note>
  Puedes nombrar los campos como quieras, solo asegúrate de que la sintaxis Liquid de tu plantilla coincida.
</Note>

<Warning>
  **Mantente por debajo de 2KB:** Si tienes carritos grandes, considera limitar a los primeros 3-5 artículos o enviar solo los campos esenciales para evitar exceder el límite de tamaño.
</Warning>

### Plantilla de email

Este ejemplo muestra cómo construir una plantilla de email que muestra:

* El conteo de artículos del carrito
* Cada producto con imagen, nombre, cantidad y precio usando un bucle for
* Un botón que enlaza a la URL única del carrito del cliente

<Frame caption="Ejemplo de plantilla de email de carrito abandonado">
  <img src="https://mintcdn.com/onesignal/KSCNwSpBCNSQ8xdF/images/docs/fd61543-Screenshot_2023-08-28_at_10.52.05_AM.png?fit=max&auto=format&n=KSCNwSpBCNSQ8xdF&q=85&s=4128b15d8d53d69c6626e129eee538fa" width="693" height="1477" data-path="images/docs/fd61543-Screenshot_2023-08-28_at_10.52.05_AM.png" />
</Frame>

<Steps>
  <Step title="Crear la plantilla de email">
    Navega a **Messages > Templates > New Email Template** y abre el Editor de Arrastrar y Soltar.
  </Step>

  <Step title="Agregar la estructura del diseño">
    Crea cinco filas:

    * Filas 1, 2 y 4: una columna con un bloque de **Párrafo**
    * Fila 3: cuatro columnas con **HTML | Párrafo | Párrafo | Párrafo**
    * Fila 5: una columna con un bloque de **Botón**

    <Frame caption="Plantilla de email inicial para carrito abandonado">
      <img src="https://mintcdn.com/onesignal/v-hm59YoewwF90UX/images/email/starter-template-abandoned-cart.png?fit=max&auto=format&n=v-hm59YoewwF90UX&q=85&s=551e9ec4618f64fdc0b6ab610a537c48" width="2968" height="2124" data-path="images/email/starter-template-abandoned-cart.png" />
    </Frame>
  </Step>

  <Step title="Mostrar el conteo de artículos">
    En la fila 1, agrega:

    ```liquid Liquid theme={null}
    We're holding onto {{message.custom_data.cart.size}} items in your cart, but don't wait too long, other squirrels are getting ahead!
    ```

    Para mejor gramática, podrías usar un condicional para decir "1 item" vs "2 items", pero para emails de carrito abandonado, el plural generalmente es aceptable.

    ```liquid Liquid theme={null}
    {% assign cart = message.custom_data.cart %}
    {% assign item_count = cart.size | plus: 0 %}
    {% if item_count == 1 %}
    We're holding onto {{item_count}} item in your cart, but don't wait too long, other squirrels are getting ahead!
    {% endif %}
    {% if item_count > 1 %}
    We're holding onto {{item_count}} items in your cart, but don't wait too long, other squirrels are getting ahead!
    {% endif %}
    ```
  </Step>

  <Step title="Iniciar el bucle">
    Usa un [bucle for](./using-liquid-syntax#for-loops) para repetir la fila de visualización del producto por cada artículo en el carrito.

    En la fila 2 (inicio del bucle), agrega esto al bloque de texto:

    ```liquid Text theme={null}
    {% for product in message.custom_data.cart %}
    ```

    **Qué hace esto:**

    * Comienza un bucle que itera sobre cada objeto en el array `cart`
    * Crea una variable temporal llamada `product` que representa el elemento actual
    * Todo lo que está entre `{% for %}` y `{% endfor %}` se repite una vez por cada artículo del carrito
    * Puedes nombrar `product` como quieras (por ejemplo, `item`, `cartItem`), solo mantén la consistencia

    <Warning>
      Ubicación del bucle for: Asegúrate de que la sintaxis `{% for %}` esté en su propia fila de bloque de texto. No la coloques dentro de una fila de múltiples columnas con otro contenido, ya que esto puede romper el renderizado del email en algunos clientes.
    </Warning>
  </Step>

  <Step title="Mostrar los detalles del producto">
    Esta fila de 4 columnas muestra la imagen, nombre, cantidad y precio. Como está dentro del bucle, se repite para cada artículo del carrito.

    En la fila 3 (detalles del producto), configura:

    **Columna 1 - Bloque HTML** (imagen del producto):

    ```html HTML theme={null}
    <img src="{{product.product_image}}" alt="Product image" style="max-width:100%;" />
    ```

    **Columnas 2–4 - Bloques de texto** (nombre del producto, cantidad, precio):

    * Columna 2: `{{product.product_name}}`
    * Columna 3: `{{product.product_quantity}}`
    * Columna 4: `{{product.product_price}}`

    **Cómo funciona el bucle:**

    * En la primera iteración, `product` = primer objeto en el array del carrito
    * `{{product.product_image}}` obtiene la URL de la imagen del primer artículo
    * En la segunda iteración, `product` = segundo objeto
    * La fila se repite automáticamente para todos los artículos del carrito

    <Warning>
      **Coincidencia de nombres de campos:** Las claves como `product_image` deben coincidir exactamente con tu payload de evento (distingue mayúsculas y minúsculas). Los nombres que no coincidan se renderizan como cadenas vacías.
    </Warning>
  </Step>

  <Step title="Finalizar el bucle">
    Cierra el bucle para marcar dónde se detiene la repetición.

    En la fila 4 (fin del bucle), agrega:

    ```liquid Liquid theme={null}
    {% endfor %}
    ```

    <Note>
      Cada `{% for %}` debe tener un `{% endfor %}` correspondiente. Si falta, se romperá el renderizado del email.
    </Note>
  </Step>

  <Step title="Agregar un botón de enlace al carrito">
    En el bloque de **Botón** de la fila 5, establece la URL de acción a:

    ```liquid Text theme={null}
    {{message.custom_data.cart_url}}
    ```

    <Frame caption="Plantilla base de email de carrito abandonado terminada sin estilos">
      <img src="https://mintcdn.com/onesignal/v-hm59YoewwF90UX/images/email/finished-template-abandoned-cart-custom-data.png?fit=max&auto=format&n=v-hm59YoewwF90UX&q=85&s=d26b638eb17878bb51c28a5e08f77724" width="2968" height="1716" data-path="images/email/finished-template-abandoned-cart-custom-data.png" />
    </Frame>
  </Step>

  <Step title="Probar la plantilla">
    * Configura tu solicitud de API usando el [Ejemplo de payload `custom_data`](#ejemplo-de-payload-custom_data)
    * Envíate el email a ti mismo.
    * Verifica que los datos se muestren correctamente.

    <Check>¡Éxito! Ahora puedes aplicar tus propios estilos a la plantilla. Consulta [Diseñar emails con arrastrar y soltar](./design-emails-with-drag-and-drop).</Check>
  </Step>
</Steps>

### Plantilla de push

Las notificaciones push tienen límites de caracteres y restricciones del sistema operativo, por lo que en lugar de mostrar todos los artículos, muestra el primer producto e indica el conteo total con gramática correcta.

Aquí tienes un ejemplo de notificación push que construiremos:

<Frame caption="Ejemplo de plantilla de push de carrito abandonado">
  <img src="https://mintcdn.com/onesignal/4HyuQPBpu-4xjmQC/images/docs/d0db985-Screenshot_2023-08-28_at_9.30.23_AM.png?fit=max&auto=format&n=4HyuQPBpu-4xjmQC&q=85&s=6cf03ce9b622ba399656f2b14fb055f3" width="688" height="656" data-path="images/docs/d0db985-Screenshot_2023-08-28_at_9.30.23_AM.png" />
</Frame>

**Campo de mensaje:**

```liquid Liquid theme={null}
{% assign cart = message.custom_data.cart %}
{% assign item_count = cart.size | plus: 0 %}
{% if item_count == 1 %}
You left {{cart.first.product_name}} in your cart.
{% endif %}
{% if item_count == 2 %}
You left {{cart.first.product_name}} and {{item_count | minus: 1}} more item in your cart.
{% endif %}
{% if item_count > 2 %}
You left {{cart.first.product_name}} and {{item_count | minus: 1}} more items in your cart.
{% endif %}
```

<Info>
  Consulta [Uso de sintaxis Liquid](./using-liquid-syntax#if-elsif-else) para más información.
</Info>

**Campo de imagen:**

```liquid Liquid theme={null}
{{message.custom_data.cart.first.product_image | default: "https://i.imgur.com/ssPCfbC.png"}}
```

<Info>
  Consulta [Imágenes de notificaciones y rich media](./rich-media) para más información.
</Info>

**Campo de URL de lanzamiento:**

```liquid Liquid theme={null}
{{cart_url | default: "https://yourdomain.com/cart"}}
```

<Check>
  Guarda la plantilla y usa su `template_id` en tu solicitud de la [API Create message](/reference/create-message) con la propiedad `custom_data` para probar.
</Check>

***

## Solución de problemas y mejores prácticas

* **Mantenlo simple**: Solo incluye datos que realmente usarás en la plantilla
* **Mantente por debajo de 2KB**: Monitorea el tamaño de tu payload, especialmente con arrays
* **Usa nombres consistentes**: Mantén `snake_case` o `camelCase` en todo el contenido
* **Valida antes de enviar**: Verifica valores nulos, arrays vacíos y campos requeridos

**Diseño de plantillas:**

* **Siempre usa filtros default** para campos opcionales:
  ```liquid Liquid theme={null}
  {{message.custom_data.user_name | default: "there"}}
  ```
* **Verifica el tamaño del array antes de iterar**:
  ```liquid Liquid theme={null}
  {% if message.custom_data.items.size > 0 %}
    {% for item in message.custom_data.items %}
      {{item.name}}
    {% endfor %}
  {% endif %}
  ```
* **Prueba con casos límite**: arrays vacíos, campos faltantes, conteos máximos de elementos

**Manejo de errores:**

* Registra las respuestas de la API en el servidor para detectar errores de validación
* Monitorea las tasas de entrega de mensajes: caídas repentinas pueden indicar errores de Liquid
* Mantén plantillas de respaldo listas para mensajes transaccionales críticos

**Rendimiento:**

* **Pre-formatea datos complejos** en tu backend en lugar de usar lógica Liquid compleja
* **Almacena plantillas en caché** y reutilízalas en múltiples llamadas de API
* Considera separar los mensajes transaccionales de alto volumen de las campañas de marketing

<Accordion title="El mensaje se envía pero el contenido está en blanco">
  **Causa:** Errores de sintaxis Liquid o nombres de campos que no coinciden

  **Soluciones:**

  * Verifica que los nombres de campos coincidan exactamente entre `custom_data` y la plantilla (distingue mayúsculas y minúsculas)
  * Busca errores tipográficos: `{{message.custom_data.name}}` no `{{message.custm_data.name}}`
  * Usa [filtros default](./using-liquid-syntax#default) para capturar campos faltantes
  * Prueba las plantillas con la estructura real de `custom_data` antes de producción
</Accordion>

<Accordion title="Error de API: El cuerpo de la solicitud es demasiado grande">
  **Causa:** `custom_data` excede el límite de 2KB

  **Soluciones:**

  * Elimina campos innecesarios de tu payload
  * Acorta nombres de campos y valores donde sea posible
  * Limita los arrays a los primeros 3-5 elementos
  * Mueve el contenido estático grande (como HTML completo) a tu plantilla en su lugar
</Accordion>

***

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Personalización de mensajes" href="./message-personalization" icon="sparkles">
    Descripción general de todas las opciones de personalización en OneSignal, incluyendo Tags, atributos de usuario y segmentación.
  </Card>

  <Card title="API Create Message" href="/reference/create-message" icon="code">
    Referencia completa de la API para enviar mensajes con custom\_data, opciones de segmentación y todos los campos disponibles.
  </Card>

  <Card title="Uso de sintaxis Liquid" href="./using-liquid-syntax" icon="droplet">
    Referencia completa de sintaxis Liquid incluyendo filtros, condicionales, bucles y manipulación de cadenas.
  </Card>

  <Card title="Templates" href="./templates" icon="file">
    Crea y gestiona plantillas de mensajes reutilizables para canales de Push, Email, SMS e In-App.
  </Card>
</Columns>

<Info>
  ¿Necesita ayuda?

  Chatee con nuestro equipo de Soporte o envíe un correo electrónico a `support@onesignal.com`

  Por favor incluya:

  * Detalles del problema que está experimentando y pasos para reproducir si están disponibles
  * Su ID de aplicación de OneSignal
  * El ID externo o ID de suscripción si corresponde
  * La URL del mensaje que probó en el panel de OneSignal si corresponde
  * Cualquier [registro o mensaje de error](/docs/es/capturing-a-debug-log) relevante

  ¡Estamos felices de ayudar!
</Info>

***
