Ahora con creación de páginas por IA vía el servidor MCP

API de entrega GraphQL

Qué consultas existen, cómo funciona el acotado por workspace y cuáles envuelve el SDK frente a las que escribes tú.

cmssy sirve contenido publicado por un único endpoint GraphQL. El SDK ya envuelve las lecturas habituales -páginas, layouts, configuración del sitio, formularios-, así que la mayoría de apps nunca escribe una consulta. Para lo demás (modelos propios, registros, listar páginas hijas) envías la tuya por el cliente de entrega.

Endpoint y acotado

Las lecturas públicas van a la ruta con organización:

{apiBase}/public/{orgSlug}/{workspaceSlug}/graphql

apiBase es tu apiUrl sin el /graphql final: por defecto https://api.cmssy.io, así que las peticiones aterrizan en https://api.cmssy.io/public/{org}/{ws}/graphql. Sobrescribe apiUrl solo si te auto-alojas. org y workspaceSlug vienen de tu configuración y el SDK arma la ruta por ti.

Como la organización va en la ruta, un slug de workspace solo tiene que ser único dentro de su organización.

Dos formas de acotar una consulta

Toda operación está acotada al workspace, pero no todas del mismo modo. Este es el detalle con el que la gente tropieza:

  • workspaceSlug (String!): para lecturas de páginas, layouts, configuración y formularios. Los helpers del SDK lo pasan automáticamente desde tu configuración.
  • workspaceId (String!): para lecturas de modelos, registros y páginas por tipo. Llama a client.queryScoped(...): si tu consulta declara $workspaceId y no lo pasas, el SDK lo resuelve desde workspaceSlug e inyecta la variable y la cabecera x-workspace-id.
// $workspaceId se rellena por ti
await client.queryScoped(MY_QUERY, { modelSlug: "products", limit: 20 });

Lo que el SDK ya envuelve

Normalmente nunca escribes estas: el helper indicado las llama por ti.

  • publicPagefetchPage: devuelve { id, blocks, publishedBlocks }.
  • publicPageByIdfetchPageById: devuelve { id, publishedBlocks }.
  • publicPagesfetchPages: devuelve [{ id, slug, updatedAt, publishedAt }].
  • publicPage (campos SEO) → fetchPageMeta: devuelve { id, seoTitle, seoDescription, seoKeywords, displayName }.
  • publicPageLayoutsfetchLayouts: devuelve [{ position, blocks }].
  • publicSiteConfigfetchSiteConfig / resolveSiteLocales: nombre del sitio, locales, funciones, branding.
  • public.form.getresolveForms (y context.forms): campos y ajustes del formulario.
  • public.form.submitSUBMIT_FORM_MUTATION: devuelve { success, message, submissionId, ... }.

Estas las escribes tú

No tienen helper en el SDK. Envíalas por client.queryScoped(...).

Registros de modelos propios

query PublicModelRecords(
  $workspaceId: String!
  $modelSlug: String!
  $filter: JSON
  $sort: String
  $limit: Int
  $offset: Int
  $populate: [String!]
) {
  public {
    model {
      records(
        workspaceId: $workspaceId
        modelSlug: $modelSlug
        filter: $filter
        sort: $sort
        limit: $limit
        offset: $offset
        populate: $populate
      ) {
        items { id modelId data status createdAt updatedAt }
        total
        hasMore
      }
    }
  }
}

Escribe la consulta tú y guárdala en tu repositorio. El SDK sí lleva cadenas equivalentes, pero bajo @cmssy/core/internal: una subruta interna, lo que significa que puede cambiar entre versiones sin nota de cambio incompatible. Tu propia consulta ocupa cuatro líneas y nunca te sorprende.

Listar páginas hijas

Esto es lo que alimenta un índice de blog o un árbol de documentación:

query PublicPagesByType(
  $workspaceId: String!
  $parentSlug: String
  $search: String
  $limit: Int
  $offset: Int
) {
  publicPagesByType(
    workspaceId: $workspaceId
    parentSlug: $parentSlug
    search: $search
    limit: $limit
    offset: $offset
  ) {
    items {
      id
      slug
      fullSlug
      publishedAt
      displayName
      seoTitle
      seoDescription
      customFields
      pageType
    }
    total
    hasMore
  }
}

Enviar un formulario

mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
  public {
    form {
      submit(formId: $formId, input: $input) {
        success
        message
        submissionId
        redirectUrl
      }
    }
  }
}

Exportada como SUBMIT_FORM_MUTATION. Pasa { formId, input: { data } }.

Auth de miembros: no la llames directamente

Las mutaciones siteMember -login, register, refresh, logout, forgotPassword, resetPassword, verifyEmail- sostienen el flujo de autenticación de miembros.

No las llames desde tu propio código. Monta createCmssyAuthRoute en su lugar: las gestiona en servidor y sella la cookie de sesión. Llamarlas directamente significa encargarte tú del sellado de tokens, y equivocarse ahí es un fallo de seguridad, no una página rota.

Cosas que conviene saber

  • data y filter usan el escalar JSON: pasa objetos planos, no JSON en cadena.
  • customFields en una página es un mapa JSON de los campos propios de ese tipo de página.
  • Las lecturas devuelven solo contenido publicado, salvo que se aporte un previewSecret válido. El SDK lo hace por ti en modo edición con tu draftSecret.

Siguientes pasos