Saltar al contenido principal

Landing Pages API

Una página de destino es una página pública que tu equipo construye en el editor de Joryio (arrastrar y soltar o HTML), publica en una URL y, opcionalmente, dota de un formulario. Un formulario enviado identifica a la persona, escribe atributos y suscripciones a listas en su perfil y registra los eventos Landing Page Viewed y Landing Page Form Submitted, que segmentos, embudos, journeys y campañas disparadas pueden usar como cualquier otro evento.

Todos los endpoints de esta página son relativos a la URL base: https://api-eu1.joryio.com/api - ver Referencia de la API.

Permisos: landing_pages:read, landing_pages:write, landing_pages:publish (ver Claves de API).

Dónde vive una página​

Una página publicada se sirve en <pages host>/<workspace handle>/<slug>. El handle del espacio de trabajo es su propio id - opaco, permanente, nunca se elige ni se cambia (https://<workspace id>.eu1.jrypages.com/<slug> en el dominio de páginas); el slug es único por espacio de trabajo y se deriva del nombre de la página salvo que lo definas. Un espacio de trabajo puede servir sus páginas en un subdominio propio en su lugar (https://pages.tumarca.com/<slug>) - consulta Usa tu propio dominio. Cada página tiene además un enlace de vista previa (?preview=<signature>, de previewUrl): el borrador actual, visible sin iniciar sesión en Joryio para quien tenga el enlace, nunca cacheado ni indexado; deja de funcionar al retirar la página.

El esquema del formulario​

form es lo que valida la ruta pública de envío. El marcado de la página solo lleva los nombres de los campos.

{
"fields": [
{ "key": "email", "type": "email", "label": "Email", "required": true },
{ "key": "first_name", "type": "text", "label": "First name", "attribute": "firstName" },
{ "key": "size", "type": "radio", "options": [{ "label": "S", "value": "s" }, { "label": "M", "value": "m" }] },
{ "key": "marketing", "type": "checkbox", "label": "Email me offers", "lists": ["<listId>"] },
{ "key": "lists", "type": "subscriptions", "lists": ["<listId>", "<listId>"] },
{ "key": "src", "type": "hidden", "value": "utm_source" }
],
"submit": { "action": "message", "message": "Thanks - check your inbox." },
"identity": "email"
}
TipoEscribeNotas
email, phoneel email / teléfono del contactocomo máximo uno de cada; phone con lists suscribe a esas listas
text, number, date, dropdown, radioel atributo nombrado en attribute (por defecto: la clave)dropdown y radio necesitan options; radio siempre es obligatorio
checkboxun atributo booleano; con lists, suscribe al marcarlolas casillas de consentimiento nunca vienen marcadas
checkbox_groupun atributo de tipo listaoptions obligatorio
subscriptionssuscribe a las listas marcadaslists obligatorio
hiddenun atributo con un value fijo, o utm_source, utm_medium, utm_campaign, utm_content, utm_term, referrer, page

submit.eventName (opcional) dispara ese evento personalizado en la persona además del evento de plataforma, con los valores enviados como propiedades; submit.setAttributes (opcional, { "lead_source": "spring_sale" }) escribe valores fijos de atributos en cada envío. Ambos pueden fijarse también en el botón de envío del editor. submit.action es message (muestra message en el sitio) o redirect (a url, solo https). Un formulario necesita un campo de email o teléfono para identificar a la persona; un enlace desde un mensaje de Joryio podrá llevar un token de persona en su lugar (previsto).

Endpoints​

MétodoRutaPermisoQué hace
GET/landing-pageslanding_pages:readLista paginada ({ data, pagination }) con estado, estadísticas, quién creó y quién editó por última vez, y URL pública. ?templates=true lista plantillas (tuyas y luego las integradas); filtros ?status= (separados por comas), ?q=, ?tags=, ?createdBy=, ?editedBy=; orden sobre toda la lista con ?sortBy= (name, status, views, submissions, createdAt, editedAt, publishedAt) y ?sortOrder=; ?limit= (máx. 100), ?offset=
GET/landing-pages/:id/historylanding_pages:readHistorial de auditoría de una página, lo más reciente primero: creación, actualización, publicación, retirada y eliminación con el actor. ?limit= 1-200
POST/landing-pageslanding_pages:writeCrea un borrador: name, slug, mode (dnd o html), html, css, grapejsData, form, seo (title, description, faviconUrl, ogImageUrl, noIndex), language, dir, tags, sdkEnabled, personalized
GET/landing-pages/:idlanding_pages:readUna página completa, con publicUrl y previewUrl
PUT/landing-pages/:idlanding_pages:writeActualiza; la página en vivo solo cambia en la siguiente publicación
DELETE/landing-pages/:idlanding_pages:writeElimina un borrador (retira primero la publicación)
POST/landing-pages/:id/duplicatelanding_pages:writeCopia una página o crea una desde una plantilla; { "asTemplate": true } guarda la copia como plantilla
POST/landing-pages/:id/publishlanding_pages:publishCongela el documento saneado y lo sirve
POST/landing-pages/:id/unpublishlanding_pages:publishLa retira; el token de firma rota
GET/landing-pages/:id/previewlanding_pages:readEl borrador renderizado como la página en vivo (HTML); sus envíos se descartan
GET/landing-pages/:id/submissionslanding_pages:readEl registro de envíos, del más reciente al más antiguo; datos personales enmascarados sin pii:view. ?limit= (máx. 200), ?before=
GET/landing-pages/:id/submissions.csvlanding_pages:readTodos los envíos como CSV en streaming, del más antiguo al más reciente: submitted_at, una columna por campo del formulario (cada respuesta, asignada o no), person_id, identified_by, lists_subscribed, referrer y los valores utm_*; datos personales enmascarados sin pii:view
GET/landing-pages/:id/reportlanding_pages:readVistas, primeras vistas y envíos por día (UTC), totales y tasa de conversión del rango, contadores acumulados. ?days= 1-365 (30 por defecto)
GET/landing-pages/:id/reviewlanding_pages:readRevisión de lanzamiento: hallazgos (code, severity blocker o recommendation, scope, params). Los bloqueadores impiden publicar; POST .../publish responde 400 con los mismos hallazgos
GET/landing-pages/handlelanding_pages:readLa dirección de páginas del espacio de trabajo: { handle, host, example, customHost }. El handle es el id del propio espacio de trabajo, la primera etiqueta de cada URL de página - opaco, permanente, nunca se elige ni se cambia; customHost es el dominio propio activo del espacio de trabajo, cuando lo tiene
GET/landing-pages/domainlanding_pages:publishEl dominio propio del espacio de trabajo: { domain, limit, used, pagesHost } - domain (o null) con id, hostname, status (pending, active, failed), verifiedAt, target, records (los registros DNS a añadir), providerConfigured, lastError; limit y used cuentan los dominios de landing pages de la organización
POST/landing-pages/domainlanding_pages:publishAñade tu propio dominio: { "hostname": "pages.tumarca.com" }. Responde el dominio como pending con sus registros DNS. 409 cuando el espacio de trabajo ya tiene uno o la organización está en su límite (el mensaje nombra el límite)
POST/landing-pages/domain/:id/verifylanding_pages:publishVuelve a comprobar el DNS y el certificado en el edge; responde el dominio con su estado actual
DELETE/landing-pages/domain/:idlanding_pages:publishQuita el dominio; las URL de las páginas vuelven de inmediato al dominio de páginas
GET/landing-pages/capabilitieslanding_pages:read{ botCheck }: si el servidor puede verificar Cloudflare Turnstile, para que un formulario pueda poner botCheck: true

Rutas públicas (sin autenticación)​

MétodoRutaQué hace
GET/p/:handle/:slugLa página publicada
POST/p/:id/viewBaliza de vista que envía la propia página
POST/p/:id/submitEnvío del formulario; el cuerpo lleva la firma incrustada en el documento publicado, así que solo la página tal como se publicó puede enviar
GET/p/go/:workspaceId/:slugAdonde apunta {{ 'slug' | landing_page_url }} en un mensaje: un 302 a la URL canónica de la página, con la cadena de consulta (token de la persona, valores UTM)
GET/p/confirm/:tokenEl enlace de un correo de doble opt-in: suscribe las listas que nombra el token y muestra una página de confirmación

Los límites de tasa se fijan por quién hace la petición, no por la dirección IP: un operador móvil o una oficina pueden poner miles de personas detrás de una dirección. La página, su baliza de vista y /p/go solo tienen un tope contra inundaciones de 30.000 peticiones por minuto por IP. Un enlace de confirmación válido nunca se limita; los no válidos se rechazan tras 300 por minuto desde una misma IP. Envíos de formularios: 15 por minuto y 60 por hora de la misma persona (email, si no teléfono) en el mismo formulario, 3.000 por minuto por página y 2.000 por minuto por IP; un envío rechazado muestra un mensaje en el idioma de la página. El cuerpo de una página está limitado a 500 KB y 50 campos. Cada formulario lleva un campo honeypot y una permanencia mínima de dos segundos.

Usa tu propio dominio​

En lugar de https://<workspace id>.eu1.jrypages.com/<slug>, un espacio de trabajo puede servir sus páginas en un subdominio de su propiedad - https://pages.tumarca.com/<slug>. El dominio se configura desde Ajustes -> Landing pages (la tarjeta "Tu dominio") o con las cuatro rutas de arriba; requiere landing_pages:publish.

  1. Añade el subdominio (POST /landing-pages/domain). Un subdominio de tu propiedad, como pages.tumarca.com - no un dominio raíz, y nunca un dominio de Joryio. Joryio lo registra en el edge y responde con el dominio en estado pending y los registros DNS que hay que configurar.
  2. Añade los registros DNS en tu proveedor de DNS: un CNAME del subdominio al destino que te da Joryio, más los registros de validación del certificado (TXT). Ajustes -> Landing pages los muestra con un botón de copiar; records lleva la misma lista.
  3. Verifica (POST /landing-pages/domain/:id/verify). En cuanto el CNAME resuelve, el certificado se emite en unos minutos y el estado pasa a active; los dominios pendientes también se vuelven a comprobar solos cada cinco minutos. Una validación que caduca pasa a failed, con el motivo en lastError - corrige los registros y verifica de nuevo, o quita el dominio y añádelo otra vez.

Mientras el dominio está active, cada URL de página que escribe Joryio - la publicUrl de una página, el enlace de vista previa, {{ 'slug' | landing_page_url }} en los mensajes y la redirección /p/go que hay detrás - lo usa; la dirección de páginas sigue sirviendo las mismas páginas, así que nada se rompe antes ni después. Quitar el dominio (DELETE /landing-pages/domain/:id) devuelve las URL a la dirección de páginas de inmediato; las páginas siguen publicadas.

Cuántos. Un espacio de trabajo tiene como máximo un dominio. Una organización puede tener por defecto un dominio de landing pages entre todos sus espacios de trabajo; GET /landing-pages/domain informa del limit y de cuántos hay used, y añadir uno por encima del límite responde 409 con un mensaje que lo nombra. Pídenos subir el límite para más espacios de trabajo o marcas.

Una lista que requiere doble opt-in (ajustes de la lista) nunca se suscribe directamente desde un formulario. La persona se escribe y el evento se registra, la respuesta indica revisar la bandeja de entrada, y un correo (en el idioma de la página, a través de la configuración de email del espacio de trabajo) lleva un enlace firmado válido siete días; GET /p/confirm/:token suscribe las listas con optInSource: double_opt_in y muestra una página de confirmación. La fila del registro las lista en listsPending; la revisión de lanzamiento dice qué listas pedirán confirmación.

form.botCheck: true muestra un widget de Cloudflare Turnstile antes del botón de envío y rechaza un envío cuyo token no se verifique. Solo está disponible cuando el servidor está configurado para Turnstile (GET /landing-pages/capabilities). Los formularios dentro de mensajes in-app nunca lo necesitan: el SDK los autentica.

Visitantes, el Web SDK y personalización​

Una página publicada cuenta por sí sola vistas, primeras vistas y envíos; a un visitante que llega por un enlace de mensaje con su token (?jp=, ver el filtro Liquid más abajo) se le escribe Landing Page Viewed en el perfil. Dos interruptores por página van más allá:

  • sdkEnabled carga el Web SDK del espacio de trabajo en la página en vivo (necesita una app Web activa en Ajustes > Apps; si no, el guardado se rechaza). Cada visitante recibe una sesión y eventos como en cualquier página con el SDK; el runtime registra Landing Page Viewed a través del SDK, un visitante con token se identifica al llegar, y un formulario enviado identifica al visitante como la persona en la que escribió, así que su historial anónimo se fusiona en el perfil. Los mensajes in-app dirigidos a la app Web pueden mostrarse en la página. La vista previa del editor nunca carga el SDK.
  • personalized renderiza el Liquid de la página para un visitante que llega con token, como lo haría un mensaje ({{ user.firstName | default: "there" }}, atributos, landing_page_url); sin token la misma página se renderiza con una persona vacía, así que se ven los valores por defecto. Un renderizado personal se sirve con Cache-Control: private, no-store; el filtro catalog no está disponible en páginas.

Plantillas, IA y enlaces desde mensajes​

Plantillas integradas. ?templates=true lista las plantillas del espacio de trabajo y cinco integradas (alta a boletín, inscripción a evento, lista de espera de producto, sorteo, próximamente). Una plantilla integrada tiene un id fijo; POST /landing-pages/:id/duplicate sobre ella crea una página normal en el espacio de trabajo. Cualquier página se convierte en plantilla con { "asTemplate": true }.

Diseño y revisión con IA (solo sesión del panel, landing_pages:write): POST /ai/pages/design con brief y, opcionalmente, language, brandVoiceId, archetype (bold, editorial, minimal, product, event), colorStrategy (restrained, committed, drenched), goal (signup, waitlist, event, download, contest, other) devuelve { name, seo, design, html, css, form, language, dir } - el modelo planifica las secciones y escribe el texto, código determinista construye el HTML sobre el Brand Kit, y nada se guarda hasta que guardes. POST /ai/pages/revise con source (el HTML de la página) e instruction devuelve { source, reply }, el documento revisado completo; una revisión que elimine el formulario o añada un script se rechaza.

Enlazar a una página desde un mensaje. En cualquier cuerpo de email, SMS o WhatsApp, {{ 'spring-sale' | landing_page_url }} genera un enlace a la página publicada con ese slug. El enlace lleva el token del destinatario (?jp=), así que la vista y el envío del formulario se escriben en esa persona antes incluso de que escriba un email; un segundo argumento añade una cadena de consulta: {{ 'spring-sale' | landing_page_url: 'utm_campaign=spring' }}. Un slug no publicado genera una cadena vacía.

Eventos. Landing Page Viewed y Landing Page Form Submitted aparecen en todos los selectores de eventos (disparadores de journeys, campañas disparadas, segmentos) bajo los eventos de plataforma, con sus propiedades.

Qué hace un envío​

  1. Valida los valores contra el esquema; los campos desconocidos se ignoran y los errores vuelven como { "ok": false, "errors": [...] }.
  2. Identifica a la persona: un perfil existente con el mismo email se actualiza; si no, se crea un perfil con un id externo generado y source landing_page:<slug>. Un email o un teléfono solo se rellena donde el perfil no tiene ninguno; una persona identificada por el token del enlace (jp) puede actualizar su propio teléfono, nunca su email.
  3. Escribe los atributos mapeados, suscribe a las listas marcadas y registra Landing Page Form Submitted con pageId, pageSlug, pageName, las claves de los campos, listsSubscribed, referrer, los valores UTM, y el email y el phone del formulario con phoneUpdated (true cuando el perfil tiene ahora el teléfono del formulario, false cuando conservó uno distinto; ausente cuando el formulario no tenía teléfono o tenía el mismo).
  4. Guarda una fila del registro (pestaña Envíos) e incrementa el contador submissions de la página.
  5. Responde { "ok": true, "message": "..." } o { "ok": true, "redirect": "https://..." }.

Ver también: Páginas de destino y formularios en la guía de usuario - la misma función desde el lado del marketer.