Una sección es una franja horizontal de la página (el hero, la tabla de precios, las FAQ) junto con todos sus bloques. Normalmente las secciones te llegan gratis dentro de /v1/pages/:marker. Este endpoint es para cuando quieres solo una porción y no la tarta entera.
Cuándo una sección gana a la página entera
Pedir la página completa sigue siendo lo normal y, para la mayoría de páginas, lo correcto: una petición y todo dentro. Una sección suelta gana en unos pocos casos concretos:
- Carga diferida por debajo del pliegue. La página es larga, el carrusel de opiniones vive por el cuarto scroll y la mayoría de visitantes nunca llega ahí. Pide la página con
?emptypara el título y las meta etiquetas, trae por marcador las secciones de la primera pantalla y carga las pesadas solo cuando el visitante se acerque a ellas. - Secciones compartidas. El footer, una franja de newsletter, un bloque de contacto “¿Te quedan dudas?” que aparece en todas las páginas. Guárdalo una vez en el CRM y pídelo por marcador donde lo necesites.
- Refrescar una sola parte. Un widget de cliente al que solo le importa una sección (por ejemplo, una zona promocional que revisas con un temporizador) no necesita descargarse la página entera cada vez.
Obtener una sección
/v1/sections/:markerhttps://back.sitecog.com/content/v1/sections/:marker
content. La forma es exactamente la misma que la de una sección dentro de la respuesta de una página, así que el mismo código de renderizado sirve para las dos.Parámetros
markerpathstringobligatoriohero o reviews. Las mismas reglas que para los marcadores de página: letras latinas, dígitos y guiones bajos, de 2 a 40 caracteres, distingue mayúsculas. La sección se busca solo por su marcador (no hay parámetro de página), así que a las secciones que pidas así dales marcadores que no se repitan en otras páginas.langquerystringopcionalPor defecto: todos los idiomas activos?lang=en, ?lang=en,de o ?lang=en&lang=de. Cada código tiene que ser un idioma activo del sitio; si no, recibes 400 unknown_lang. Mira Idiomas y fallbacks.emptyqueryflagopcionalPor defecto: desactivadocontent: solo id, marker, name, index y show. Presente = activado: ?empty, ?empty=1, ?empty=true; se desactiva con 0 o false. Útil para una comprobación barata de “¿esta sección está activada?” antes de cargar nada pesado.x-crm-keyheaderstringobligatorio?key= si no puedes usar cabeceras. Mira Claves del sitio.Ejemplo de petición
curl "https://back.sitecog.com/content/v1/sections/reviews?lang=en" \
-H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"const res = await fetch('https://back.sitecog.com/content/v1/sections/reviews?lang=en', {
headers: { 'x-crm-key': 'pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40' },
});
if (!res.ok) throw new Error(`Content API: ${res.status}`);
const section = await res.json();
if (section.show) {
console.log(section.content.reviews_title.content.en); // "What people say"
}'use client';
import { useEffect, useRef, useState, type ReactNode } from 'react';
// La clave del sitio es pública por diseño, así que en el navegador no pasa nada
const KEY = process.env.NEXT_PUBLIC_CRM_KEY!;
export function LazySection({ marker, lang, render }: {
marker: string;
lang: string;
render: (section: any) => ReactNode;
}) {
const ref = useRef<HTMLDivElement>(null);
const [section, setSection] = useState<any>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting) return;
io.disconnect(); // cargar una sola vez
const res = await fetch(`https://back.sitecog.com/content/v1/sections/${marker}?lang=${lang}`, {
headers: { 'x-crm-key': KEY },
});
if (res.ok) setSection(await res.json());
}, { rootMargin: '400px' }); // empezar un poco antes de que sea visible
io.observe(el);
return () => io.disconnect();
}, [marker, lang]);
return <div ref={ref}>{section?.show ? render(section) : null}</div>;
}Ejemplo de respuesta
{
"id": 47,
"marker": "reviews",
"name": "Customer reviews",
"index": 4,
"show": true,
"content": {
"reviews_title": {
"id": 120,
"marker": "reviews_title",
"name": "Title",
"type": "text",
"multilang": true,
"updatedAt": "2026-09-21T11:02:15.000Z",
"content": { "en": "What people say" }
},
"reviews_list": {
"id": 121,
"marker": "reviews_list",
"name": "Reviews",
"type": "array",
"multilang": false,
"updatedAt": "2026-09-25T08:40:03.000Z",
"content": [
{
"author": { "en": "Maria, Berlin" },
"text": { "en": "Finally I can hear my podcast on the U-Bahn." },
"rating": 5
}
]
}
}
}Con ?empty la misma petición devuelve solo la parte de arriba: muy práctico cuando lo único que quieres saber es si la sección está activada:
{
"id": 47,
"marker": "reviews",
"name": "Customer reviews",
"index": 4,
"show": true
}Campos de la respuesta
idnumbermarkerstringnamestringindexnumbershowbooleancontent{ [blockMarker]: Block }?empty.content →
idnumbermarkerstringreviews_title.namestringtypestringtext, html, image, video, link, number, color, date, date_range, boolean, object, array. Determina la forma de content.multilangbooleanupdatedAtstring (ISO 8601)contentdepends on type{ file } para una sola imagen, un array de elementos para array, etc. Todas las formas están en la página Tipos de bloque.El flag show: oculto no es borrado
Los editores pueden desactivar una sección en el CRM sin borrarla: una promo de temporada, unas FAQ a medio escribir o un bloque que espera el visto bueno de legal. La API sigue devolviendo esa sección con "show": false y todo su contenido. Decidir no renderizarla es trabajo de tu plantilla:
const reviews = await getSection('reviews');
return (
<>
<Hero />
{reviews.show && <Reviews section={reviews} />}
</>
);Errores
| Estado | Cuerpo | Qué ha pasado |
|---|---|---|
| 400 | {"message":"invalid_marker"} | El marcador tiene caracteres fuera de A–Z a–z 0–9 _ o una longitud incorrecta. |
| 400 | {"message":"invalid_lang", …} | Un código de lang está mal formado (piensa en english en vez de en). |
| 400 | {"message":"unknown_lang", …} | Un idioma de lang no está activo en el sitio. El cuerpo incluye los disponibles. |
| 401 | {"message":"invalid_key"} | Falta la clave, está mal formada o se ha revocado. |
| 404 | {"message":"section_not_found"} | No hay ninguna sección con este marcador en este sitio. ¿Una errata? ¿Otra clave de sitio? |
| 429 | {"message":"rate_limit_exceeded"} | Demasiadas peticiones en este minuto. Mira Límites de peticiones. |
La lista completa, con soluciones, está en la página de Errores.