Cómo vincular contenido del CMS a través de la API de Webflow

Cómo vincular contenido del CMS mediante la API de Webflow

Aprende a convertir slugs en IDs de elementos y a escribir en campos de Referencia y Referencia múltiple mediante la API de datos de Webflow.

Cómo vincular contenido del CMS mediante la API de Webflow

Ismail Ajagbe
Redactor técnico
Ver perfil del autor
Ismail Ajagbe
Redactor técnico
Ver perfil del autor
Índice de contenidos

Convierte nombres de autores y etiquetas de temas en referencias funcionales del CMS de Webflow con una sola ruta de Webflow Cloud.

El contenido importado suele incluir detalles legibles para humanos, como "escrito por Priya Shah" o "etiquetado como precios, incorporación", pero los campos de Referencia del CMS de Webflow requieren IDs de elementos en lugar de nombres de texto.

Vincular algunos artículos manualmente en el Designer es sencillo, pero las importaciones masivas desde hojas de cálculo o fuentes externas requieren un sistema automatizado para convertir valores de texto en IDs de elementos válidos del CMS.

En esta guía, crearemos un Route Handler de Next.js en Webflow Cloud que resuelve los slugs de autores y temas a IDs de elementos, los escribe a través de la API de datos y publica el resultado. Me centro en los slugs porque los editores cambian los nombres de visualización con mucha más frecuencia.

¿Qué necesitas para vincular elementos del CMS a través de la API de Webflow?

Aquí tienes cinco requisitos necesarios:

  • Sitio de Webflow: En el plan Premium Site o superior, necesitas montar la aplicación en tu dominio personalizado.
  • Webflow Cloud: Habilitado en ese sitio.
  • Aplicación Next.js 15: Creada con npx create-next-app@15 y gestionada con npm, ya que Webflow Cloud solo admite el gestor de paquetes npm.
  • Desarrollo local: Node.js 22 o posterior en tu equipo para el desarrollo local.
  • Acceso al sitio y colecciones: Acceso a nivel de sitio que te permita generar un token de API en la configuración del sitio, además de una colección de Artículos, una de Autores y una de Temas con algunos elementos cada una.

Con estos requisitos previos listos, ya puedes configurar el entorno y empezar a crear la integración.

6 pasos para vincular elementos del CMS con la API de Webflow en Webflow Cloud

La compilación consiste en un pequeño cliente de la API de datos y un Route Handler. El controlador verifica un secreto compartido y luego resuelve los slugs a IDs de elementos antes de escribir y publicar los campos de Autor y Temas del artículo.

1. Genera un token de API del sitio con acceso al CMS

Crea un token con alcance para este sitio antes de tocar el esquema. Lo usaré para confirmar los slugs de los campos en el siguiente paso. En la configuración del sitio, busca el área de acceso a la API y genera un token de API del sitio con acceso de lectura y escritura al CMS.

Asígnale un nombre que indique para qué sirve, como "Ruta de vinculación del CMS". Copia el token de inmediato y guárdalo en un lugar privado.

La ruta necesita acceso de lectura para listar elementos y resolver slugs, y acceso de escritura para actualizar campos y publicar. Si trabajas para un cliente, genera el token desde el sitio del cliente y registra quién es el propietario.

Si regeneras el token, actualiza la variable de entorno WEBFLOW_API_TOKEN que configuraste en el entorno de Webflow Cloud. Exporta el token guardado en tu terminal para realizar pruebas.

2. Añade campos de referencia y léelos desde el esquema

La colección de Artículos necesita un campo de Referencia llamado Autor que apunte a Autores y un campo de Referencia múltiple llamado Temas que apunte a Temas. Añade ambos en la configuración de la colección de Artículos y copia los tres ID de colección desde la configuración de cada colección en el panel del CMS.

Confirmo los slugs de los dos campos frente al esquema de la colección porque esta ruta utiliza y como claves, no como nombres visibles.authortopics

Ejecuta esta solicitud con el token de API de tu sitio para leer el esquema de la colección desde Obtener colección :

export WEBFLOW_API_TOKEN="paste-your-token"
export ARTICLES_COLLECTION_ID="paste-articles-collection-id"

curl -s "https://api.webflow.com/v2/collections/$ARTICLES_COLLECTION_ID" \
  -H "Authorization: Bearer $WEBFLOW_API_TOKEN" \
  -H "accept: application/json"

En la respuesta, busca los campos y y comprueba su tipo y la colección de destino. Según la referencia de tipos de campo de Webflow, un campo de Referencia toma un ID de elemento como cadena y un campo de Referencia múltiple toma una matriz de cadenas de ID de elemento, que es lo que envía la ruta:authortopics

Tipo de campo Formato que envía la ruta Cómo lo construye la ruta
Referencia (elemento único) Un ID de elemento como cadena Envía el ID del elemento resuelto
Referencia múltiple Una matriz de cadenas de ID de elemento Fusiona los ID existentes con los nuevos antes de escribir
Tipo de campo → Formato que envía la ruta → Cómo lo construye la ruta
Referencia (elemento único)
Un ID de elemento como cadena
Envía el ID del elemento resuelto
Referencia múltiple
Una matriz de cadenas de ID de elemento
Fusiona los ID existentes con los nuevos antes de escribir

Si tu esquema muestra slugs distintos a y , cambia las claves y en para que coincidan antes de implementar.authortopicsfieldData.authorfieldData.topicsroute.ts

3. Almacena el token y los ID de colección como variables de entorno de Webflow Cloud

Saca todas las credenciales y los ID del código y llévalos al entorno de Webflow Cloud donde se ejecuta la aplicación.

La documentación de variables de entorno de Webflow Cloud indica: "Tanto las variables de entorno secretas como las no secretas están disponibles para el proceso de compilación de tu aplicación y para la aplicación implementada en tiempo de ejecución". Los secretos se ocultan en los registros de compilación, por lo que el token y el secreto de la ruta reciben la configuración de secreto.

Añade cada variable al entorno desde el que se implementa tu aplicación de Webflow Cloud y refleja los mismos valores en un archivo local para ..env.localnext dev

Lectura de las variables de entorno necesarias

La ruta lee cinco variables:

  • WEBFLOW_API_TOKEN: El token de API del sitio, almacenado como un secreto para que los registros de compilación lo oculten; omitir el prefijo NEXT_PUBLIC_ evita que aparezca en el código del navegador.
  • LINK_ROUTE_SECRET: Una cadena larga y aleatoria que generas tú mismo; los emisores la envían en una cabecera para que nadie pueda reescribir tus relaciones del CMS.
  • ARTICLES_COLLECTION_ID: El ID de la colección que contiene los campos de Autor y Temas, que es el elemento que actualiza la ruta.
  • AUTHORS_COLLECTION_ID: La colección a la que apunta el campo Autor, utilizada para convertir un slug de autor en el ID del elemento que almacena el campo.
  • TOPICS_COLLECTION_ID: La colección detrás del campo Temas, utilizada para resolver cada slug de tema antes de combinar los IDs en la lista existente.

Mantengo estas variables en el lado del servidor omitiendo NEXT_PUBLIC_ de sus nombres. Next.js expone las variables con ese prefijo al código del navegador; usarlo para el token daría a cualquiera que vea el código fuente acceso de escritura a tu CMS. Una vez guardada, la ruta tiene todo lo que necesita en tiempo de ejecución.

4. Escribe un pequeño cliente de API de datos para elementos del CMS

Crea lib/webflow.ts para mantener las llamadas a la API de datos en un solo módulo. Los agrupo aquí para que el manejador de rutas sea legible y cada solicitud comparta la misma autenticación y gestión de errores. Utiliza fetch estándar, por lo que no es necesario instalar ningún SDK.

El cliente cubre las tres llamadas que necesita la ruta:

// lib/webflow.ts
const API_BASE = "https://api.webflow.com/v2";

export type CmsItem = {
  id: string;
  isDraft: boolean;
  isArchived: boolean;
  fieldData: Record<string, unknown> & { slug: string; name: string };
};

type ListItemsResponse = {
  items: CmsItem[];
  pagination: { limit: number; offset: number; total: number };
};

function authHeaders(): Record<string, string> {
  const token = process.env.WEBFLOW_API_TOKEN;
  if (!token) throw new Error("WEBFLOW_API_TOKEN is not set");
  return {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    accept: "application/json",
  };
}

async function webflowRequest<T>(
  path: string,
  init: { method?: string; body?: string } = {}
): Promise<T> {
  const res = await fetch(`${API_BASE}${path}`, {
    method: init.method ?? "GET",
    body: init.body,
    headers: authHeaders(),
  });
  if (!res.ok) {
    const detail = await res.text();
    throw new Error(`Webflow API ${res.status} on ${path}: ${detail}`);
  }
  return (await res.json()) as T;
}

// Returns the one item with this slug, using the API's exact-match slug filter.
export async function itemBySlug(
  collectionId: string,
  slug: string
): Promise<CmsItem | undefined> {
  const page = await webflowRequest<ListItemsResponse>(
    `/collections/${collectionId}/items?slug=${encodeURIComponent(slug)}&limit=1`
  );
  return page.items[0];
}

export async function updateItemFields(
  collectionId: string,
  itemId: string,
  fieldData: Record<string, unknown>
): Promise<CmsItem> {
  return webflowRequest<CmsItem>(
    `/collections/${collectionId}/items/${itemId}`,
    { method: "PATCH", body: JSON.stringify({ fieldData }) }
  );
}

export async function publishItems(
  collectionId: string,
  itemIds: string[]
): Promise<unknown> {
  if (itemIds.length === 0) return null;
  return webflowRequest(`/collections/${collectionId}/items/publish`, {
    method: "POST",
    body: JSON.stringify({ itemIds }),
  });
}

La actualización utiliza PATCH y envía solo los campos de Autor y Temas, por lo que la solicitud nunca toca los otros campos del artículo. Cada búsqueda utiliza el filtro de coincidencia exacta List Items del endpoint, por lo que una solicitud típica cuesta un puñado de pequeñas búsquedas sin importar cuán grandes sean las colecciones.slug 

Esto es importante porque cada llamada cuenta para el límite de velocidad por minuto de la API de datos, que cubre la guía de errores 429 . Un error lanzado lleva el estado exacto y el cuerpo de respuesta de Webflow.

5. Crea el manejador de rutas que escribe y publica los enlaces

Crea el endpoint que resuelve los slugs y combina la lista de Temas antes de escribir y publicar el artículo. Añade app/api/link/route.ts, y omite cualquier runtime exportación. Webflow Cloud se ejecuta en Cloudflare Workers, una plataforma perimetral, pero el objetivo de tiempo de ejecución perimetral de Next.js es una configuración separada que el adaptador de OpenNext Cloudflare no admite.

El manejador compara la cabecera x-link-secret en tiempo constante y mantiene el token en el servidor. No tiene límite de velocidad, así que llámalo solo desde herramientas de confianza:

// app/api/link/route.ts
import { NextResponse } from "next/server";
import { itemBySlug, updateItemFields, publishItems } from "@/lib/webflow";

type LinkRequest = {
  articleSlug: string;
  authorSlug?: string;
  topicSlugs?: string[];
};

function safeEqual(a: string, b: string): boolean {
  const enc = new TextEncoder();
  const x = enc.encode(a);
  const y = enc.encode(b);
  let diff = x.length ^ y.length;
  const len = Math.max(x.length, y.length);
  for (let i = 0; i < len; i++) diff |= (x[i] ?? 0) ^ (y[i] ?? 0);
  return diff === 0;
}

function env(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is not set`);
  return value;
}

export async function POST(request: Request) {
  const secret = process.env.LINK_ROUTE_SECRET ?? "";
  const provided = request.headers.get("x-link-secret") ?? "";
  if (!secret || !safeEqual(provided, secret)) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  let body: LinkRequest;
  try {
    body = (await request.json()) as LinkRequest;
  } catch {
    return NextResponse.json({ error: "Body must be JSON" }, { status: 400 });
  }
  if (!body.articleSlug) {
    return NextResponse.json({ error: "articleSlug is required" }, { status: 400 });
  }

  try {
    const articlesId = env("ARTICLES_COLLECTION_ID");
    const authorsId = env("AUTHORS_COLLECTION_ID");
    const topicsId = env("TOPICS_COLLECTION_ID");
    const topicSlugs = body.topicSlugs ?? [];

    const [article, author, topicItems] = await Promise.all([
      itemBySlug(articlesId, body.articleSlug),
      body.authorSlug ? itemBySlug(authorsId, body.authorSlug) : Promise.resolve(undefined),
      Promise.all(topicSlugs.map((slug) => itemBySlug(topicsId, slug))),
    ]);

    if (!article) {
      return NextResponse.json({ error: `Unknown article: ${body.articleSlug}` }, { status: 404 });
    }

    const fieldData: Record<string, unknown> = {};
    const authorIds: string[] = [];
    const topicIds: string[] = [];

    if (body.authorSlug) {
      if (!author) {
        return NextResponse.json({ error: `Unknown author: ${body.authorSlug}` }, { status: 404 });
      }
      fieldData.author = author.id;
      authorIds.push(author.id);
    }

    if (topicSlugs.length) {
      const missing = topicSlugs.filter((_, i) => !topicItems[i]);
      if (missing.length) {
        return NextResponse.json({ error: `Unknown topics: ${missing.join(", ")}` }, { status: 404 });
      }
      for (const item of topicItems) topicIds.push(item!.id);
      const existing = Array.isArray(article.fieldData.topics)
        ? (article.fieldData.topics as string[])
        : [];
      fieldData.topics = Array.from(new Set([...existing, ...topicIds]));
    }

    if (Object.keys(fieldData).length === 0) {
      return NextResponse.json({ error: "Nothing to link" }, { status: 400 });
    }

    await updateItemFields(articlesId, article.id, fieldData);

    // Publish the linked authors and topics, then the article.
    await publishItems(authorsId, authorIds);
    await publishItems(topicsId, topicIds);
    await publishItems(articlesId, [article.id]);

    return NextResponse.json({ linked: body.articleSlug, fieldData });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown error";
    return NextResponse.json({ error: message }, { status: 502 });
  }
}

Escribí la comparación a mano porque timingSafeEqual existe en el tiempo de ejecución de Workers desplegado pero no bajo next dev local, por lo que una ruta que lo usa pasa en producción y falla en tu portátil, o viceversa. La combinación Set es lo que mantiene los temas existentes. La ruta también pasa cada ID de autor y tema vinculado a publishItems.

Publicar esos elementos también pone en vivo cualquier otra edición guardada en ellos. Si los editores guardan cambios en Autores o Temas, elimina esas dos llamadas publishItems y publica solo el artículo. Una solicitud correcta ahora actualiza los campos del artículo y lo publica en una sola solicitud a la ruta.

6. Prueba la ruta desplegada bajo su ruta de montaje

Llama a la ruta desplegada con un artículo real más su autor y dos temas, usando la URL completa incluyendo la ruta de montaje que elegiste para la aplicación de Webflow Cloud. Yo uso la ruta de montaje más /api/link, donde reside la ruta, en lugar de /api/link en la raíz del sitio. Establece APP_URL en el dominio de tu sitio más la ruta de montaje, sin barra diagonal al final.

Envía esta solicitud desde tu terminal, intercambiando los slugs que existan en tus colecciones:

export APP_URL="https://www.example.com/content-tools"
export LINK_ROUTE_SECRET="paste-your-route-secret"

curl -s -X POST "$APP_URL/api/link" \
  -H "Content-Type: application/json" \
  -H "x-link-secret: $LINK_ROUTE_SECRET" \
  -d '{"articleSlug":"pricing-update","authorSlug":"priya-shah","topicSlugs":["pricing","onboarding"]}'

Una operación exitosa devuelve un JSON con configurado en el slug del artículo y un objeto que muestra un ID de autor y los IDs de los temas combinados. Ejecuta la misma solicitud una segunda vez y la matriz de temas no debería aumentar, ya que la combinación elimina los duplicados.linkedfieldData

Luego abre el artículo en el panel del CMS: el autor mostrará a Priya Shah, los temas enumerarán ambas etiquetas además de las que ya estaban allí y, si tu plantilla de artículos vincula los campos de autor y temas, la página de colección en vivo los mostrará.

¿Qué hace que los enlaces del CMS de la API de Webflow fallen?

Los fallos se dividen en cuatro grupos: valores que la API de datos rechaza por ser referencias no válidas, compilaciones dañadas por la exportación del entorno de ejecución edge de Next.js, solicitudes que omiten la ruta de montaje de la aplicación y enlaces que se guardan pero nunca llegan al sitio publicado.

La ruta devuelve un 502 con un error de Webflow en el campo de autor o temas

Causa: El valor en no es un ID de elemento de la colección a la que apunta ese campo. Los culpables habituales son enviar un nombre o slug en lugar de un ID, usar un ID de una colección diferente, identificar el valor por nombre para mostrar () en lugar de por slug (), o enviar una sola cadena a un campo de referencia múltiple que espera una matriz.fieldDataAuthorauthor

Solución: Empiezo con el cuerpo de la respuesta 502, que contiene el estado y el texto de error que envió Webflow. Vuelve a ejecutar la solicitud GET en la colección de artículos y compara el ID de colección al que apunta cada campo de relación con y en tu entorno.AUTHORS_COLLECTION_IDTOPICS_COLLECTION_ID

Si nombra una colección diferente a la que apunta el campo de autor, la búsqueda del slug puede resolver un elemento de la colección incorrecta; verifica ese ID contra el objetivo del campo. Intercambiar los IDs de autores y temas puede fallar antes con una respuesta 404 si no se encuentra el slug del autor solicitado. Confirma que el valor de los temas sea una matriz incluso cuando vincules un solo tema.AUTHORS_COLLECTION_IDUnknown author

La compilación en Webflow Cloud falla en una ruta que se compila localmente

Causa: La página de Webflow bring-your-own-app te indica que agregues la directiva del entorno de ejecución edge de Next.js a las rutas de la API, y seguirla rompe la compilación.

Webflow Cloud implementa Next.js a través del adaptador OpenNext Cloudflare, que no admite el destino de tiempo de ejecución de Next.js. La confusión proviene de que la palabra "edge" significa dos cosas en la misma página: Webflow Cloud se ejecuta en Cloudflare Workers, una plataforma edge, mientras que es una configuración de Next.js que el adaptador no puede compilar.edgeexport const runtime = 'edge'

Solución: Elimina la exportación de cada archivo de ruta y vuelve a implementar. Yo uso el tiempo de ejecución predeterminado de Next.js, que el adaptador compila cuando la exportación está ausente. Si también proteges rutas con middleware, mantén el archivo llamado ; cambiarle el nombre a en Next 16 lo mueve al tiempo de ejecución de Node, que Webflow Cloud no admite para middleware.runtimemiddleware.tsproxy.ts

La ruta devuelve un 404 desde las propias páginas de la aplicación pero funciona con curl

Causa: La ruta de montaje se inyecta en el momento de la compilación y las llamadas del lado del cliente no se reescriben para incluirla. Una llamada del navegador a desde una página dentro de tu aplicación se resuelve en la raíz del sitio, fuera de la ruta de montaje de la aplicación, por lo que la solicitud nunca llega a la ruta. La prueba con curl pasa porque escribiste la ruta de montaje en tú mismo.fetch/api/linkAPP_URL

Solución: Agrega una variable de entorno no secreta llamada que contenga la ruta de montaje, como , ya que Webflow Cloud no la crea, y construye las URLs de solicitud del cliente como . Las llamadas del lado del servidor dentro de la aplicación no se ven afectadas. Mantén la ruta secreta en el servidor y llámala solo desde el servidor o una herramienta interna de confianza.NEXT_PUBLIC_BASE_PATH/content-tools${process.env.NEXT_PUBLIC_BASE_PATH}/api/link

Los enlaces aparecen en el panel del CMS pero no en el sitio publicado

Causa: La escritura del campo y las llamadas de publicación son solicitudes separadas. La ruta envía el a través de primero, luego realiza tres llamadas de . Si el tiene éxito y una de esas llamadas de publicación falla, la ruta devuelve un 502 después de que la escritura del campo ya se haya realizado. El panel del CMS muestra entonces el nuevo autor y los temas; espera que la página en vivo siga mostrando la última versión publicada hasta que una llamada de publicación tenga éxito.PATCHupdateItemFieldspublishItemsPATCH

Solución: Lee el cuerpo del 502 para ver el texto de error de Webflow y la ruta de la solicitud. Yo uso el ID de colección en esa ruta para identificar si los autores, los temas o los artículos no se pudieron publicar. Una vez corregida la causa, vuelve a ejecutar la misma solicitud; la combinación hace que el reintento sea seguro, porque los temas no se duplicarán.Set

Lo que puedes construir a continuación con la API de Webflow y el CMS de Webflow

Esa misma resolución de slug a ID es el núcleo de una importación programada que vincula cada artículo nuevo al publicarse, o de una tarea de limpieza que elimina un tema retirado de todas las listas de referencia múltiple a la vez. Para mover colecciones enteras dentro y fuera de Webflow, la guía de base de datos externa utiliza la misma API de datos.

Los elementos de lista ahora también se filtran por campos personalizados con filter[<slug>][<op>], por lo que una búsqueda por cualquier campo único, como el correo electrónico de un autor, funciona igual que el filtro de slug en esta ruta.

Para una personalización más profunda más allá de lo que los campos de referencia y referencia múltiple manejan de forma nativa, la documentación para desarrolladores de Webflow cubre la publicación de elementos y el resto de la API de datos.

Preguntas frecuentes

¿Necesito Webflow Cloud para vincular elementos del CMS a través de la API?

No. Cualquier servidor que pueda almacenar un token y enviar solicitudes HTTPS puede llamar a la API de datos, incluido un script local. Webflow Cloud es práctico porque la ruta se despliega junto a tu sitio y lee los secretos de su entorno, así evitas ejecutar un servidor independiente para un solo endpoint.

¿Puedo proteger la ruta de vinculación con el middleware de Next.js en lugar de una verificación de encabezado?

Sí, siempre que el archivo se llame middleware.ts. Webflow Cloud solo admite middleware de tiempo de ejecución Edge en Workers. Next 16 cambia el nombre del middleware a proxy.ts, pero ese archivo se ejecuta en el tiempo de ejecución de Node y no puede optar por Edge, por lo que un archivo renombrado no funcionará. Recomiendo mantener el nombre original al actualizar Next.js.

¿Cómo elimino un tema de un artículo?

Para eliminar un tema, lee la matriz topics actual del artículo, filtra el ID del elemento no deseado y envía la matriz recortada a través de updateItemFields. Luego llama a publishItems para el artículo, de modo que la página en vivo elimine el tema. Enviar a mi ruta menos slugs de temas nunca elimina ninguno, porque solo fusiona nuevos IDs en la lista existente del artículo.

¿Puede la ruta leer un CSV de enlaces desde el disco para procesarlo de forma masiva?

No. El tiempo de ejecución de Workers de Webflow Cloud no tiene acceso al sistema de archivos, como señala su página de compatibilidad con Node.js, por lo que la ruta no puede leer un CSV desde el disco. Envía las filas en el cuerpo de la solicitud o recupera el archivo a través de HTTPS, luego ejecuta la misma lógica para cada fila.


Última actualización
October 24, 2026
Categoría

Artículos relacionados


verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logoreddit logothe new york times logoideo logoupwork logodiscord logo
verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logoreddit logothe new york times logoideo logoupwork logodiscord logo

Empieza gratis

Prueba Webflow todo el tiempo que quieras con nuestro plan Starter gratuito. Compra un plan de sitio de pago para publicar, alojar y desbloquear funciones adicionales.

Empieza, es gratis
Ver demostración

Prueba Webflow todo el tiempo que quieras con nuestro plan Starter gratuito. Compra un plan de sitio de pago para publicar, alojar y desbloquear funciones adicionales.