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

# Crear y actualizar categorías

> Crea o actualiza categorías en lote. Si una categoría con el mismo nombre ya existe, se actualiza.

Puedes crear o actualizar categorías en lote según su nombre. Si una categoría con el mismo nombre ya existe en la misma tienda y sucursal, se actualiza; si no, se crea.

<ParamField path="app_id" type="string" required>
  ID de tu tienda en Jelou Shop.
</ParamField>

<ParamField body="resources" type="object[]" required>
  Lista de categorías a crear o actualizar (máx. 500 por solicitud).

  <Expandable title="Campos de cada categoría">
    <ParamField body="name" type="string" required>
      Nombre de la categoría (máx. 255 caracteres). Se usa como identificador único dentro de la tienda y sucursal.
    </ParamField>

    <ParamField body="description" type="string">
      Descripción de la categoría.
    </ParamField>

    <ParamField body="image" type="string">
      URL **pública** de la imagen de la categoría. No se aceptan URLs privadas/internas.
    </ParamField>

    <ParamField body="order" type="integer" default="0">
      Posición de ordenamiento de la categoría (mín. 0, máx. 999999). Cuando es mayor que 0, debe ser único dentro del mismo nivel (app + categoría padre + sucursal). El valor `0` se trata como "sin orden".
    </ParamField>

    <ParamField body="status" type="boolean" default="true">
      Estado de la categoría (activa/inactiva).
    </ParamField>

    <ParamField body="branch" type="string">
      Código de la sucursal a la que pertenece la categoría. La sucursal debe existir previamente.
    </ParamField>

    <ParamField body="parent_id" type="integer">
      ID de la categoría padre (para jerarquía). Debe existir previamente en la misma tienda.
    </ParamField>
  </Expandable>
</ParamField>

<Warning>
  La sucursal debe estar creada antes de asignarla a una categoría. Usa el endpoint [Crear sucursales](/guides/integraciones/e-commerce/api-sucursales/crear) para registrarla primero.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://gateway.jelou.ai/ecommerce/v2/apps/{app_id}/batch/categories/upsert" \
    -H "x-api-key: TU_CLAVE_API" \
    -H "Content-Type: application/json" \
    -d '{
      "resources": [
        {
          "name": "Pizzas",
          "description": "Todas nuestras pizzas artesanales",
          "image": "https://ejemplo.com/images/pizzas.jpg",
          "order": 1,
          "status": true,
          "branch": "SUC-CENTRO"
        },
        {
          "name": "Bebidas",
          "description": "Bebidas frías y calientes",
          "order": 2,
          "branch": "SUC-CENTRO"
        },
        {
          "name": "Postres",
          "order": 3
        }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 202 Accepted theme={null}
  {
    "message": "Batch upsert process initiated successfully",
    "count": 3,
    "jobs": 1
  }
  ```

  ```json 422 Validation Error theme={null}
  {
    "message": "Each category must have a name.",
    "errors": {
      "resources.0.name": ["Each category must have a name."]
    }
  }
  ```
</ResponseExample>

## Comportamiento

<AccordionGroup>
  <Accordion title="Procesamiento asíncrono">
    El endpoint retorna `202 Accepted` inmediatamente. Las categorías se procesan en segundo plano.
  </Accordion>

  <Accordion title="Upsert por nombre">
    Las categorías se identifican por su `name` dentro de la misma tienda y sucursal. Si ya existe una con ese nombre, se actualiza en lugar de crear una nueva.
  </Accordion>

  <Accordion title="Sucursal inexistente">
    Si el código de sucursal no coincide con ninguna sucursal de la tienda, la categoría se crea sin sucursal asignada.
  </Accordion>

  <Accordion title="Imágenes">
    Si se proporciona una URL de imagen, se descarga y almacena en segundo plano después de la creación de la categoría. La URL debe ser pública; las URLs privadas/internas se rechazan.
  </Accordion>

  <Accordion title="Jerarquía (parent_id)">
    Usa `parent_id` para anidar categorías. El padre debe existir en la misma tienda antes de procesarse el lote. Para construir una jerarquía nueva, crea primero las categorías padre y luego envía las hijas referenciando su `parent_id`.
  </Accordion>

  <Accordion title="Orden único por nivel">
    Cuando `order` es mayor que 0, debe ser único dentro del mismo nivel (app + categoría padre + sucursal). Si dos categorías del mismo lote comparten `order` en el mismo nivel, o si choca con una categoría existente, la solicitud falla con `422`. El `order` 0 o nulo se considera "sin orden" y no valida unicidad.
  </Accordion>
</AccordionGroup>

## Errores de validación

<Accordion title="Mensajes de error comunes">
  | Campo                   | Mensaje                                                                                                                    |
  | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- |
  | `resources`             | The resources field is required.                                                                                           |
  | `resources`             | At least one category is required.                                                                                         |
  | `resources`             | Cannot process more than 500 categories at once.                                                                           |
  | `resources.*.name`      | Each category must have a name.                                                                                            |
  | `resources.*.image`     | Category image must be a valid URL.                                                                                        |
  | `resources.*.order`     | Category order must be an integer. / Category order cannot be negative. / Category order cannot exceed 999999.             |
  | `resources.*.order`     | Order {n} is duplicated within this batch for the same parent/branch. / Order {n} is already assigned to another category. |
  | `resources.*.status`    | Category status must be a boolean.                                                                                         |
  | `resources.*.parent_id` | Parent category id must be an integer. / The parent category does not exist for this app.                                  |
  | `resources`             | This app already has {n} categories. Adding {m} new categories would exceed the limit of {limit}.                          |
</Accordion>

## Límites

* Máximo **500 categorías** por solicitud.
* Límite total de categorías **por tienda** (configurable; por defecto **500**). Si el lote supera el límite restante, la solicitud falla con `422`.
* Todas las categorías se validan antes de procesarse.

<Note>
  Reemplaza `{app_id}` con el ID de tu tienda y `TU_CLAVE_API` con tu [clave API](/guides/configuracion/claves-api).
</Note>
