Guía del usuario del plugin de WordPress
Esta guía tiene cuatro secciones. §1 Guía rápida te lleva paso a paso a publicar tu primer shortcode con capturas de pantalla, mientras que §2–§4 son las referencias más profundas a las que volverás cuando estés creando o resolviendo problemas.
1. Guía rápida
Abre 3Min API → + New en el administrador de WordPress para lanzar el Snippet Builder. Siguiendo los cuatro pasos de arriba abajo completas un shortcode.
¿Eres nuevo? Haz el tour de 3Min API para ver cómo se emiten los endpoints y las API keys, o busca "3Min API" en ChatGPT Apps y empieza la conversación directamente ahí.
1.1 Configurar y probar el endpoint

- Pega tu endpoint ID y API key, y elige un método HTTP (Get Record / Get List / POST).
- Pulsa Test endpoint — se dispara una petición real y la respuesta aparece inline justo debajo.
- Cuando la respuesta se vea bien, las siguientes tarjetas (variables, plantillas) se rellenan automáticamente.
Los endpoints POST no se pueden previsualizar desde el builder. Guarda el shortcode primero, ponlo en una página y envía el formulario para verificarlo.
1.2 Variables

El plugin lee tu respuesta de prueba y muestra las variables disponibles del template como chips. Las más comunes:
{{response}}— la respuesta completa{{response.payload}}— el cuerpo del registro de usuario{{response.payload.title}},{{response.id}}— accede en profundidad con "notación de punto"
Pon estas variables directamente en tu HTML/CSS en el siguiente paso; los valores se sanean automáticamente.
1.3 Elige una plantilla + diseña con IA

- Elegir una plantilla específica para el método rellena automáticamente los paneles HTML/CSS y JavaScript.
- El botón Copy AI prompt en la parte superior empaqueta el código de la plantilla seleccionada, las reglas de creación del plugin y las variables detectadas en un system prompt copiado al portapapeles.
- Pégalo en ChatGPT / Claude / Gemini e itera con naturalidad — "hazlo más minimalista", "modo oscuro por defecto", "muestra valoraciones con estrellas", etc.
- Vuelca el resultado en el panel HTML/CSS (y en el panel JavaScript si hace falta), y comprueba la vista previa en vivo a la derecha.
El panel JavaScript es opcional. Solo los administradores del sitio pueden guardar JavaScript.
1.4 Guardar + publicar

- Introduce un nombre de snippet (no se puede cambiar después del primer guardado).
- Pulsa Save — el builder genera un shortcode con este formato:
[3minapi name="your-snippet-name"] - Pega esa línea en cualquier entrada, página, widget o bloque, y tu UI se renderiza ahí mismo.
El mismo shortcode funciona en varias páginas, y una sola página puede contener varios shortcodes diferentes — cada uno se renderiza de forma independiente.
2. Guía de HTML/CSS
Cuando guardas, parte del HTML/CSS se elimina automáticamente por seguridad. Las siguientes secciones describen qué sobrevive al guardado y cómo escribir respetando ese contrato.
2.1 Cómo funciona el renderizado
- Get Record: cuando se carga la página, el servidor llama a la API, el HTML/CSS se renderiza con la respuesta inline, y luego se ejecuta tu JavaScript.
- Get List: mismo flujo que Get Record. Solo la primera página se renderiza en el servidor — las siguientes llegan vía
threeminApi.fetch()(botones de "Load more" y similares). - POST: el HTML/CSS del formulario se renderiza primero; la llamada a la API y los handlers de JavaScript se ejecutan cuando el usuario envía. (Tu JavaScript en sí se ejecuta justo después del render para registrar los handlers — el envío luego los dispara.)
- Cada shortcode se auto-envuelve en tiempo de renderizado con
<div class="threemin-api-rendered" data-3minapi-preset="<snippet>">…</div>. Usa ese atributo para encontrar el root desde JavaScript — no añadasdata-3minapi-preseta tu propio HTML.
2.2 Etiquetas y CSS permitidos
| Categoría | Permitido |
|---|---|
| Etiquetas de texto | h1–h6, p, div, span, blockquote, ul / ol / li, familia de tablas, a, img, figure, figcaption |
| Etiquetas de formulario | form, input, button, textarea, select, option, fieldset, legend, label |
| Estilos | bloques <style> |
| CSS | Color / tipografía, modelo de caja, layout (Flexbox + Grid), movimiento (transform, transition, animation, filter), interacción (pointer-events, cursor, user-select) |
| Mostrar/ocultar | atributo hidden, clase con display:none en <style>, inline style="display:none" |
| Eliminado al guardar | etiquetas <script>, etiquetas PHP (<?php, <?=), handlers de eventos inline (onclick, onerror, …), URLs javascript: |
Algunas propiedades style inline se eliminan al guardar — mantén las reglas CSS dentro de un bloque <style>.
2.3 Patrones recomendados
- HTML5 semántico — usa niveles de heading, listas y etiquetas de tabla para datos tabulares; empareja
figureconfigcaptionpara imágenes con leyenda. - Nombres de clase BEM con el nombre del snippet como prefijo — ej.
.product-card,.product-card__title. Varias instancias del mismo shortcode en una página no chocarán. - Un bloque
<style>por snippet. - Layout moderno — Flex / Grid /
gapestán todos permitidos. - Unidades responsivas —
rem,%,vw,vh. - Imágenes con lazy-load —
<img loading="lazy">. - Accesibilidad —
aria-label,aria-hidden,role="..."siguen funcionando tras guardar. - Validación de formularios HTML5 —
required,min,max,pattern,type=email/number/date/…se aplican tal cual.
2.4 Patrones a evitar
- ⚠️
data-3minapi-*personalizados en etiquetas de formulario (la causa nº 1 de fallos silenciosos — sin error tampoco). Añadir cualquier atributodata-*no whitelisted a<button>/<input>/<textarea>/<form>/<select>/<fieldset>se elimina silenciosamente al guardar. TuquerySelector('[data-...]')devuelvenull, no se vincula ningún listener, y nada se registra en consola. Usa clases BEM para tus elementos de interacción como botones e inputs. Etiquetas normales como<div>/<span>/<li>aceptan cualquierdata-*. - Los selectores deben coincidir exactamente con tu HTML — una sola errata rompe silenciosamente.
- Usa
<button>en lugar de<div role="button">. - No hagas layout con
style="..."inline — mantén las reglas de layout en un bloque<style>. - No apliques estilos con
id— los IDs colisionan cuando el mismo shortcode aparece dos veces en una página. Usa clases. - Nada de anchos
pxfijos en contenedores top-level — rompen layouts responsivos.
2.5 Consejos por método
2.5.1 Get Record — inyectar campos de la API en HTML
- Después de ejecutar Test endpoint, la tarjeta Variables muestra dos chips:
{{response}}(la respuesta completa) y{{response.payload}}(el registro del usuario). - Accede a campos anidados con notación de punto:
{{response.payload.title}},{{response.id}},{{response.payload.author.name}}. - Los valores de las variables se sanean automáticamente.
- Atributos de vista/edición (whitelisted para etiquetas de formulario):
data-3minapi-view-mode,data-3minapi-edit-mode,data-3minapi-edit-toggle,data-3minapi-save,data-3minapi-cancel,data-3minapi-delete. Los inputs dentro del formulario de edición usan unname="..."normal.
2.5.2 Get List — renderizar items dinámicamente
- Pon solo un contenedor vacío en tu HTML — JavaScript añade cada fila en tiempo de ejecución. Las filas hardcodeadas se duplicarían en la página publicada.
- DOM hooks whitelisted:
<ul data-3minapi-feed-list>(contenedor de items),<button data-3minapi-feed-more>(Load more),[data-3minapi-feed-empty](estado vacío). - Chips de variables:
{{response.data}}(el array — se sustituye por vacío en el template, solo referencia),{{response.pagination.next_cursor}},{{response.data.0.payload.x}}(un único registro por índice, para áreas de feature estáticas). - El renderizado de múltiples filas y la paginación van en el panel JS.
2.5.3 POST — conectar un formulario a tu endpoint
- Consulta las definiciones de campos del body de tu endpoint en el panel de 3Min API.
- Haz que el atributo
namede cada input coincida exactamente con un nombre de campo del body. - Auto-binding del submit: pon
<form data-3minapi-form>dentro de[data-3minapi-preset]y el runtime intercepta el evento submit. Las plantillas POST por defecto ya lo incluyen. - Feedback de estado automático: pon
<div data-3minapi-status></div>dentro del formulario. El runtime escribetextContenty alterna las clasesis-success/is-error. - Los atributos de validación HTML5 (
required,min,max,pattern,type=email/number/date/…) se aplican todos — validación ligera sin JS. - Usa el panel JavaScript para validación personalizada, envío condicional o lifecycle hooks.
3. Guía de JavaScript
El panel JavaScript es opcional, y solo los administradores del sitio pueden guardarlo. El código que pegues aquí se ejecuta automáticamente justo después de que se renderiza el HTML/CSS del shortcode.
3.1 Cómo se ejecuta
- El runtime envuelve tu código como
(function(scope){ /* your code */ })('<snippet>')automáticamente. No hacen falta etiquetas<script>, imports ni wrappers IIFE. - La variable
scopese inyecta automáticamente — úsala directamente. No hardcodees el nombre del snippet como cadena literal. - Si el mismo shortcode se renderiza varias veces en una página, cada instancia se ejecuta de forma independiente con su propio scope.
- Todos los helpers viven en
window.threeminApi.*. Para rutas exactas de campos de respuesta y código de ejemplo completo, mira los bloques[Sample —]en la salida de Copy AI prompt.
3.2 Entorno de ejecución
- JavaScript de navegador estándar (ES2017+) —
async/await,fetch,Promise, arrow functions, destructuring, todo funciona. - APIs del DOM directamente —
document.querySelector,addEventListener,createElement, etc. - Nada de
importdentro del snippet. Carga librerías externas víawp_enqueue_scripten tu tema/plugin y accede a ellas como globales (window.X). - Solo los administradores del sitio pueden guardar JavaScript. Otros campos como HTML/CSS los pueden guardar usuarios no-admin.
3.3 Helpers de un vistazo
| Helper | Modo | Qué hace |
|---|---|---|
data(scope) |
All | Parsea la respuesta inline (sin llamada de red). Devuelve null si falta. |
fetch(scope, q) |
Get List | Trae la siguiente página vía proxy del servidor. { cursor: <next_cursor> }. |
fetchById(scope, id) |
Get List | Hace GET de un único registro por id. |
submit(scope, body) |
POST | Auto-bound cuando hay <form data-3minapi-form> — rara vez se llama directamente. |
update(scope, body) |
Get Record | PUT del registro al que apunta el snippet. |
updateById(scope, id, body) |
Get List | PUT de un único registro por id. |
delete(scope) |
Get Record | DELETE del registro al que apunta el snippet. |
deleteById(scope, id) |
Get List | DELETE de un único registro por id. |
on(scope, event, cb) |
POST | Registra hooks 'before-submit' / 'success' / 'error'. |
Una mutación devuelve un acuse de recibo de la cola, no el registro actualizado. Las respuestas de
update/delete/updateById/deleteByIdllevansuccess,message,id(un id de tarea de cola, no un id de registro) yqueued_at— eso es todo. Actualiza el DOM de forma optimista con el body que acabas de enviar. La escritura real del registro puede retrasarse hasta ~3 segundos respecto al siguiente GET.
3.4 Recomendado / a evitar
Recomendado
- Encontrar el root una sola vez —
var root = document.querySelector('[data-3minapi-preset="' + scope + '"]');y luego limita el alcance de cada selector conroot.querySelector(...). Seguro incluso cuando el mismo shortcode se renderiza varias veces. - Delegación de eventos — para filas añadidas dinámicamente, ata un listener en el contenedor e identifica la fila con
e.target.closest('.…'). - Identidad por fila vía dataset — graba
li.dataset.recordId = item.iduna vez, y leee.target.closest('li').dataset.recordIden el handler. - Las lecturas usan la caché —
threeminApi.data(scope)solo parsea el JSON inline, así que llamarlo repetidamente no cuesta nada. - Las respuestas de mutación son acuses —
update/deletesolo devuelven un acuse de la cola, así que actualiza el DOM con el body que enviaste (UI optimista).
Evitar
- ⚠️ Componer ids de elementos concatenando
scopeyrecord_id— el nombre del snippet y el id de registro upstream son cadenas completamente distintas. - ⚠️ Profundidad de wrap incorrecta en respuestas de mutación — success vive en
res.json.data.data.success. Leerres.json.data.success(un.datade menos) es siempre falsy → HTTP 200 acaba en la rama de error, la trampa más común. - Leer el registro actualizado de la respuesta de una mutación — el acuse de cola no tiene contenido del registro.
- Hacer un re-fetch inmediato después de una mutación — el procesamiento asíncrono puede retrasarse ~3s. Usa
setTimeout(..., 3000)con un indicador "Saving…". - Envolver el body de la mutación como
{ payload: { ... } }— el body es un objeto plano{ field: value }. Los objetos anidados desaparecen silenciosamente durante la codificación URL. - Sintetizar
record_id— debe coincidir con[A-Za-z0-9_-], ≤256 caracteres. El servidor devuelve 422 ante violación. Reutiliza el id que la API devolvió en una respuesta anterior. - Hardcodear el nombre del snippet como literal — usa la variable
scopeauto-inyectada.
3.5 Profundidad de wrap de la respuesta — la trampa común
wp_send_json_success de WordPress envuelve una vez, y el handler admin-ajax del plugin envuelve otra vez. Entonces:
- GET único inline (
data(scope)) —data(scope).data.payload.X. - Siguiente página de
fetch()— la respuesta completa está enres.json.data.data(dos.datas). - Registro único de
fetchById()— el registro está enres.json.data.data.data(tres.datas). - Acuse de cola de mutación —
res.json.data.data.{success, message, id, queued_at}(dos.datas). - Evento del hook
'success'de POST —e.response.data.data.{success, …}(dos.datas).
Para firmas completas y código de ejemplo probado, mira los bloques [Sample —] en la salida de Copy AI prompt.
4. FAQ
4.1 "El clic en el botón no hace nada, y tampoco hay error en consola"
Añadiste un atributo data-* a una etiqueta de formulario (<button>, <input>, <form>, <select>, <fieldset>). El filtro de seguridad de WordPress (wp_kses) lo eliminó al guardar, por lo que querySelector('[data-...]') devuelve null, el listener nunca se vincula, y nada se registra.
Solución — cambia a una clase BEM en ese elemento (<button class="my-card__back">) y búscalo con root.querySelector('.my-card__back').
4.2 "Tarjeta vacía / los campos se renderizan como undefined"
Estás leyendo la ruta incorrecta en la respuesta. La ruta exacta de cada helper está documentada en los bloques [Sample —] del AI prompt. Respuestas correctas frecuentes:
data(scope).data.payload.X— GET único.res.json.data.data.data.payload.X—fetchById(envuelto tres veces).
4.3 "Update devolvió HTTP 200 pero la vista no cambió"
Estás comprobando res.json.data.success. Esa ruta es un .data menos y siempre es undefined para mutaciones. El flag de success de la mutación está en res.json.data.data.success (WP envuelve una vez, el plugin envuelve otra). Lo mismo para delete / updateById / deleteById. Para hooks POST 'success' la ruta equivalente es e.response.data.data.success.
4.4 "La lista se renderiza vacía aunque la API devolvió 200"
Tu selector de DOM hook no coincide exactamente con el HTML. Vuelve a leer el HTML y confirma que data-3minapi-feed-list (o tu hook personalizado) aparece literalmente.
4.5 "Una regla CSS no se aplica tras guardar"
O la regla era inline y el filtro de seguridad la eliminó, o la propiedad no está en la lista de CSS seguro. Muévela dentro de un bloque <style>.
4.6 "El POST se envió bien, pero no aparece feedback"
No hay ningún <div data-3minapi-status></div> dentro de tu formulario. El runtime escribe textContent y alterna is-success / is-error solo en ese elemento.
4.7 "Update tuvo éxito pero la UI sigue mostrando el valor antiguo"
Estás intentando leer el registro actualizado de la respuesta de la mutación. Una mutación solo devuelve un acuse de cola — sin contenido del registro. Actualiza el DOM directamente con el body que acabas de enviar.
4.8 "Incluso tras refrescar, los resultados de la mutación siguen mostrando datos obsoletos"
Las mutaciones se procesan de forma asíncrona, y la siguiente lectura puede retrasarse ~3 segundos. Usa UI optimista. Si realmente necesitas re-fetch, hazlo con setTimeout(..., 3000) y un indicador "Saving…".
4.9 "El panel JS está deshabilitado con un aviso"
Tu cuenta no tiene permiso para guardar JavaScript (habitual en multisite o roles restringidos). Guarda el snippet sin JavaScript, o pide a un administrador del sitio que guarde la parte de JavaScript.
4.10 "Test endpoint devuelve un error de red"
Comprueba el endpoint ID y la API key en tu panel de 3Min API.
Dónde obtener ayuda
- Haz un tour de 3Min API — 3minapi.com/tour
- Contacto directo — contact@3minapi.com
- Foro de soporte de WordPress.org — disponible una vez que el plugin esté publicado, en la pestaña Support de la página del directorio.