Estructura de la respuesta
Todos los endpoints devuelven la misma estructura. Cambia el identificador de la página; el resto es idéntico.
{ "page": "product", "product_id": "333333174", "sections": {}}| Campo | Tipo | Presente en |
|---|---|---|
page | home · category · product | Todas las respuestas de contenido. |
product_id | string | Solo cuando page es product y pediste un producto concreto. |
category_id | string | Solo cuando page es category y pediste una categoría concreta. |
sections | objeto | Siempre. Puede venir vacío ({}). |
sections es un objeto indexado por clave, no un array. Si no hay contenido
configurado, es {} — nunca null.
Anatomía de la clave
Sección titulada «Anatomía de la clave»Cada clave de sections codifica cuatro datos:
faq_with_image-product-333333174-cmqic4kt5000ieq2bk3hbiat3└──────┬─────┘ └──┬──┘ └───┬────┘ └────────────┬─────────┘ │ │ │ │ kind placement targetId blockId| Parte | Qué es |
|---|---|
kind | El tipo de sección, en minúsculas y con guiones bajos: faq, faq_with_image, image_carousel, rich_description, grid_promocional, call_to_action_banner, specs, specs_faq, key_features_banner, promo_modal, featured_benefits, video_banner, parallax_banner, video_reviews. |
placement | La página donde vive. Solo tres valores: home, category, product — los mismos que page. |
targetId | El ID de la entidad, o el literal general cuando la sección no apunta a una entidad concreta. |
blockId | Identidad estable de esa instancia. Es un cuid. |
El blockId es estable
Sección titulada «El blockId es estable»No cambia cuando el comerciante reordena las secciones ni cuando borra otra.
Puedes usarlo como key de render, como ancla de URL o como identificador de
caché sin que se rompa en la próxima carga.
Identificar el tipo de una sección
Sección titulada «Identificar el tipo de una sección»Hay dos formas. Usa la primera.
Recomendado: el campo tipo
Sección titulada «Recomendado: el campo tipo»Cada sección trae un discriminador explícito con el valor crudo del tipo, en mayúsculas:
{ "tipo": "RICH_DESCRIPTION", "titulo": "Características técnicas", "items": []}Eso te deja despachar sin tocar la clave:
const renderers = { FAQ: renderFaq, FAQ_WITH_IMAGE: renderFaqConImagen, IMAGE_CAROUSEL: renderCarrusel, RICH_DESCRIPTION: renderDescripcion, GRID_PROMOCIONAL: renderGrilla, CALL_TO_ACTION_BANNER: renderCta, SPECS: renderSpecs, SPECS_FAQ: renderSpecsFaq, KEY_FEATURES_BANNER: renderCaracteristicas, PROMO_MODAL: renderPromo, FEATURED_BENEFITS: renderBeneficios, VIDEO_BANNER: renderVideoBanner, PARALLAX_BANNER: renderParallaxBanner, VIDEO_REVIEWS: renderTestimonios,};
for (const seccion of visiblesEnOrden(bundle)) { renderers[seccion.tipo]?.(seccion);}Observa que tipo usa MAYUSCULAS_CON_GUION_BAJO mientras que el kind de la
clave usa minusculas_con_guion_bajo. Son el mismo valor en dos convenciones.
Alternativa: parsear la clave
Sección titulada «Alternativa: parsear la clave»Si necesitas el tipo a partir de la clave, la única forma correcta es la comparación por token exacto:
// ❌ MAL — `faq` también matchea `faq_with_image`Object.keys(bundle.sections).filter((k) => k.startsWith('faq'));
// ✅ BIEN — comparación por token exactoObject.keys(bundle.sections).filter((k) => k.split(`-${bundle.page}-`)[0] === 'faq');El separador que usas para cortar es -{placement}-, no un guion suelto: los
propios kind llevan guiones bajos. Cortar por el placement completo es lo
único que desambigua — y como bundle.page es exactamente el placement, no
cuesta nada.
El helper seccionesDeTipo(bundle, kind) que aparece en los ejemplos de toda la
documentación combina las dos vías —tipo primero, clave como respaldo— y de paso
aplica la regla de render y el orden. Está escrito una sola vez, en
el módulo cliente.
Campos comunes a toda sección
Sección titulada «Campos comunes a toda sección»Sin importar el tipo, toda sección trae estos tres:
| Campo | Tipo | Significado |
|---|---|---|
tipo | string | Discriminador del tipo, en mayúsculas (FAQ, RICH_DESCRIPTION, …). |
estado | { lifecycle, storefrontVisible } | La compuerta de render. Ver abajo. |
orden | number | Posición entre secciones de todos los tipos. Es el orden que el comerciante armó arrastrando en el panel. |
indice | number | ausente | Número de instancia (1-based) dentro del mismo tipo y alcance. Solo aparece cuando hay dos o más. |
titulo | string | null | Título que puso el comerciante. |
nombre_personalizado | string | ausente | Nombre con el que el comerciante renombró la sección. Aparece solo si la renombró. |
items | array | Siempre presente. Puede estar vacío, nunca es null. |
titulo y nombre_personalizado no son lo mismo
Sección titulada «titulo y nombre_personalizado no son lo mismo»titulo es el encabezado que se muestra en la tienda. nombre_personalizado es
la etiqueta interna con la que el comerciante identifica esa sección en su panel
cuando tiene varias del mismo tipo.
Para renderizar usa titulo. nombre_personalizado te sirve si estás
armando una herramienta de previsualización o un selector, no para la vitrina.
orden es entre tipos, no dentro del tipo
Sección titulada «orden es entre tipos, no dentro del tipo»Si el comerciante dejó un carrusel arriba de un FAQ, el carrusel tiene orden
menor. Para respetar la maquetación que armó, ordena todas las secciones
juntas por orden y solo entonces despacha por tipo:
export function visiblesEnOrden(bundle) { return Object.entries(bundle.sections) .filter(([, s]) => s.estado.lifecycle === 'active' && s.estado.storefrontVisible) .sort(([, a], [, b]) => a.orden - b.orden) .map(([key, seccion]) => ({ key, ...seccion }));}
for (const seccion of visiblesEnOrden(bundle)) { renderizar(seccion.tipo, seccion);}La respuesta ya viene emitida en orden ascendente de orden, así que en la
práctica el orden de inserción del objeto coincide. Aun así ordena
explícitamente: depender del orden de claves de un objeto JSON es frágil, y
cualquier JSON.parse intermedio o serialización de terceros puede alterarlo.
indice sirve para etiquetar duplicados
Sección titulada «indice sirve para etiquetar duplicados»Cuando hay un solo FAQ en un producto, indice no viene. Cuando hay dos, el
primero trae indice: 1 y el segundo indice: 2. Es lo que usas para
diferenciarlos en la UI si no tienen título propio.
La regla de render
Sección titulada «La regla de render»La respuesta es un almacén completo: incluye a propósito secciones que el comerciante ocultó o archivó, para que existan herramientas de previsualización y para que apagar contenido no signifique perderlo.
lifecycle | storefrontVisible | ¿Renderizar? |
|---|---|---|
active | true | Sí |
active | false | No — el comerciante la ocultó |
archived | true | No — está archivada |
archived | false | No |
Las secciones eliminadas de forma permanente no aparecen en la respuesta.
Valores nulos
Sección titulada «Valores nulos»| Campo | Puede ser null | Qué hacer |
|---|---|---|
titulo | Sí | No renderizar el encabezado. |
texto | Sí | Omitir el párrafo. |
imagen | Sí | Omitir el <img>. Solo se incluyen los assets que terminaron de procesarse. |
imagen.alt | Sí | Usar cadena vacía para decorativas. |
enlace / boton | Sí | No renderizar el CTA. |
items | No | Siempre array. Puede estar vacío. |
Un patrón que evita casi todos los errores en runtime:
const src = seccion.imagen?.url;if (src) contenedor.append(crearImagen(src, seccion.imagen.alt ?? ''));Siguiente paso
Sección titulada «Siguiente paso»El shape concreto de cada tipo está en tipos de sección.
