# luno — Full API Reference for AI Agents > Extended Public + Agent API reference. **Start with the curated index:** https://doc.luno.rest/llms.txt > Product overview, free pricing & security: https://luno.rest/llms.txt > Docs start paths: **A Agents (MCP)** · **B Console** · **C API only** — see https://doc.luno.rest/llms.txt LUNO is a **free AI-era backend platform** on Cloudflare Workers + PostgreSQL (Hyperdrive): Headless CMS capabilities, MCP for AI agents, and secure Public / Agent APIs. Public API base (recommended): `https://api.luno.rest/public/p/{projectId}/v1`; host-based: `https://{your-domain}/public/v1` (no auth by default). Agent API: `https://api.luno.rest/admin/v1` with `Authorization: Bearer sk-agent-…`. **MCP clients (Verified):** Claude Code · Cursor · Codex. LUNO does not build or host frontend code — agents operate it as the backend. Keywords for discovery: backend, backend platform, MCP, Headless CMS, free, AI agent, secure, security, Cloudflare Workers, Public API, Agent API, system of record, Agents, Console, API only, Contact Form, Embed, Webhooks, Masters, localization, Next.js, Astro, Nuxt, llms.txt. --- ## Content Model ``` Form Set (content type, identified by slug, e.g. "blog") └── Entry (individual content item, identified by slug) └── Revision (versioned content; only "published" revisions returned) └── Snapshot (field values at this revision) ``` Only entries with a `published` revision appear in the Public API responses. --- ## All Endpoints ### GET /form-sets/{slug} Returns form set metadata and the published content of the primary entry. Primary entry preference order: slug `main` → slug `_legacy` → oldest entry. Query parameters: - `locale` (string, optional) — filter localized field values (e.g., "ja", "en") Response: ```json { "formSet": { "id": "uuid", "slug": "string", "name": "string", "description": "string|null" }, "entry": { "id": "uuid", "slug": "string" }, "revision": { "id": "uuid", "revision": 3, "updatedAt": "ISO8601" }, "data": { "": "" }, "mediaUrls": { "": "https://..." } } ``` --- ### GET /form-sets/{formSetSlug}/entries Returns a paginated list of published entries in a form set. Query parameters: - `page` (integer, default 1) — page number, 1-based - `limit` (integer, default 20, max 100) — items per page - `offset` (integer) — alternative to `page`; byte offset into result set - `locale` (string) — filter localized field values - `q` (string) — full-text search keyword (Business plan+) - `sort` (string) — sort key, e.g., `created_at:desc`, `updated_at:asc`, `published_date:asc` - `include_snapshot` (boolean, default false) — include `snapshot` (field values) and `mediaUrls` in each item Response: ```json { "formSet": { "id": "uuid", "slug": "string", "name": "string", "description": "string|null" }, "total": 42, "limit": 20, "offset": 0, "items": [ { "entry": { "id": "uuid", "slug": "string" }, "published": { "revisionId": "uuid", "revision": 3, "updatedAt": "ISO8601", "snapshot": { "": "" }, // only if include_snapshot=true "mediaUrls": { "": "https://..." } // only if include_snapshot=true } } ] } ``` --- ### GET /form-sets/{formSetSlug}/entries/{entrySlug} Returns full published content for a specific entry. Behavior: if the entry's slug has changed, returns HTTP 301 with `Location` header pointing to the new URL. Clients should follow redirects. Query parameters: - `locale` (string, optional) — filter localized field values Response: ```json { "formSet": { "id": "uuid", "slug": "string", "name": "string" }, "entry": { "id": "uuid", "slug": "string" }, "revision": { "id": "uuid", "revision": 3, "updatedAt": "ISO8601" }, "data": { "": "" }, "mediaUrls": { "": "https://..." }, "widgetRoles": { "": "title|thumbnail|description|date|category" } } ``` --- ### GET /content/by-path Look up content by an import path (set during content migration from external systems). Query parameters: - `path` (string, required) — the import path value - `locale` (string, optional) Returns same structure as single entry endpoint. --- ### GET /content/by-slug Look up content using form set slug and entry slug as query parameters. Query parameters: - `formSetSlug` (string, required) - `slug` (string, required) - `locale` (string, optional) Returns same structure as single entry endpoint. --- ### GET /content/by-external-id Look up content by external system entity identifier. Query parameters: - `sourceType` (string, required, max 50) — external system name, e.g., "wordpress", "shopify" - `entityType` (string, required, max 200) — entity type, e.g., "post", "product" - `externalId` (string, required, max 2000) — the external system's identifier Returns same structure as single entry endpoint. --- ### GET /preview/revisions Fetch an unpublished revision using a signed JWT token (generated from the admin panel). Query parameters: - `token` (string, required) — JWT signed with the server's secret, valid 15 minutes Returns 401 after token expiry. Returns same structure as single entry endpoint. Works for all revision statuses including `draft`. --- ### GET /media/{assetId} Serve a media file (image, document, etc.) from Cloudflare R2. Response headers: - `Content-Type`: MIME type of the file - `Cache-Control: public, max-age=31536000` — 1-year CDN + browser cache - `ETag`: opaque identifier for conditional requests Accepts `Accept: image/webp` header — returns WebP-optimized image when available. --- ### GET /sitemap.xml XML sitemap (Sitemap Protocol 0.9) covering all published entries across all form sets. Includes ``, ``, ``, and ``. --- ### GET /form-sets/{slug}/sitemap.xml XML sitemap for a single form set's published entries only. --- ### GET /form-sets/{formSetSlug}/entries/{entrySlug}/schema.json schema.org JSON-LD structured data for the entry. Suitable for `