Headlessly
Content

Site

Site configuration -- branding, language, visibility, and rendering context for content.

Schema

import { Noun } from 'digital-objects'

export const Site = Noun('Site', {
  name: 'string!',
  subdomain: 'string##',
  title: 'string',
  description: 'string',
  tagline: 'string',
  logo: 'string',
  favicon: 'string',
  primaryColor: 'string',
  accentColor: 'string',
  status: 'Draft | Published | Maintenance',
  visibility: 'Public | Private | Password',
  ogImage: 'string',
  content: '<- Content.site[]',
  defaultLanguage: 'string',
  supportedLanguages: 'string',
  timezone: 'string',
})

Fields

FieldTypeRequiredDescription
namestringYesDisplay name of the site
subdomainstring (unique, indexed)NoSubdomain identifier (e.g., blog for blog.acme.dev)
titlestringNoSite title for browser tabs and SEO
descriptionstringNoSite-level meta description
taglinestringNoShort tagline or motto
logostringNoURL to the site logo
faviconstringNoURL to the site favicon
primaryColorstringNoPrimary brand color (hex, e.g., #0066FF)
accentColorstringNoAccent color for highlights and CTAs
statusenumNoDraft, Published, or Maintenance
visibilityenumNoPublic, Private, or Password-protected
ogImagestringNoDefault Open Graph image for social sharing
content<- Content[]NoAll content published to this site
defaultLanguagestringNoDefault language code (e.g., en)
supportedLanguagesstringNoComma-separated language codes (e.g., en,es,fr)
timezonestringNoIANA timezone for scheduled publishing (e.g., America/New_York)

Relationships

FieldDirectionTargetDescription
content<-Content.site[]All content items published to this site

Verbs

VerbEventDescription
createCreatedCreate a new site configuration
updateUpdatedUpdate site settings
deleteDeletedSoft-delete the site

Site does not define custom verbs. Status changes happen through update:

import { Site } from '@headlessly/content'

// Put site into maintenance mode
await Site.update('site_jK8sTxJv', { status: 'Maintenance' })

// Publish the site
await Site.update('site_jK8sTxJv', { status: 'Published' })

Status State Machine

              create()
(none) ──────────────→ Draft

            update()     │
              ┌──────────┤
              ▼          ▼
          Published  Maintenance
              │          │
              └──────────┘
               update() (bidirectional)

Valid transitions:

FromVerbTo
--createDraft
DraftupdatePublished
DraftupdateMaintenance
PublishedupdateMaintenance
MaintenanceupdatePublished
PublishedupdateDraft

Cross-Domain Patterns

Site is the top-level container that determines how content renders and where it lives:

  • Content: Every Content entity belongs to a Site. The site provides branding, language, and SEO defaults that content inherits.
  • Analytics: Site-level metrics aggregate across all content -- total views, unique visitors, popular pages.
  • Marketing: Sites serve as landing page destinations for Campaigns and Forms.
  • CRM: Organization-scoped sites enable white-label publishing for customers or partners.
import { Site } from '@headlessly/content'

Site.created((site, $) => {
  // Auto-create a welcome page for new sites
  $.Content.create({
    title: 'Welcome',
    slug: 'welcome',
    body: `# Welcome to ${site.name}\n\nYour site is ready.`,
    type: 'Page',
    site: site.$id,
    status: 'Draft',
    visibility: 'Public',
  })
})

Query Examples

SDK

import { Site } from '@headlessly/content'

// Find all published sites
const sites = await Site.find({ status: 'Published' })

// Get a specific site with its content
const site = await Site.get('site_jK8sTxJv', {
  include: ['content'],
})

// Create a new site
await Site.create({
  name: 'Developer Blog',
  subdomain: 'dev',
  title: 'Acme Developer Blog',
  description: 'Technical articles, tutorials, and updates from the Acme engineering team.',
  primaryColor: '#0066FF',
  status: 'Draft',
  visibility: 'Public',
  defaultLanguage: 'en',
  timezone: 'America/New_York',
})

MCP

headless.ly/mcp#search
{
  "type": "Site",
  "filter": { "status": "Published", "visibility": "Public" }
}
headless.ly/mcp#fetch
{ "type": "Site", "id": "site_jK8sTxJv", "include": ["content"] }
headless.ly/mcp#do
const sites = await $.Site.find({ status: 'Published' })
await $.Site.update('site_jK8sTxJv', { status: 'Maintenance' })

REST

# List published sites
curl https://headless.ly/~acme/sites?status=Published

# Get a specific site
curl https://headless.ly/~acme/sites/site_jK8sTxJv

# Create a site
curl -X POST https://headless.ly/~acme/sites \
  -H 'Content-Type: application/json' \
  -d '{"name": "Developer Blog", "subdomain": "dev", "status": "Draft", "visibility": "Public"}'

# Update site status
curl -X PUT https://headless.ly/~acme/sites/site_jK8sTxJv \
  -H 'Content-Type: application/json' \
  -d '{"status": "Published"}'

On this page