Headlessly
Content

Asset

Managed media files -- images, videos, documents, and audio with metadata and processing.

Schema

import { Noun } from 'digital-objects'

export const Asset = Noun('Asset', {
  name: 'string!',
  filename: 'string!',
  url: 'string!',
  type: 'Image | Video | Document | Audio | Archive | Other',
  mimeType: 'string!',
  extension: 'string',
  size: 'number!',
  width: 'number',
  height: 'number',
  alt: 'string',
  caption: 'string',
  duration: 'number',
  thumbnail: 'string',
  tags: 'string',
  uploadedBy: '-> Contact',
  source: 'string',
  license: 'string',
  process: 'Processed',
})

Fields

FieldTypeRequiredDescription
namestringYesDisplay name of the asset
filenamestringYesOriginal filename including extension
urlstringYesPublic URL to access the asset
typeenumNoImage, Video, Document, Audio, Archive, or Other
mimeTypestringYesMIME type (e.g., image/webp, video/mp4)
extensionstringNoFile extension without dot (e.g., webp, mp4)
sizenumberYesFile size in bytes
widthnumberNoWidth in pixels (images and videos)
heightnumberNoHeight in pixels (images and videos)
altstringNoAlternative text for accessibility
captionstringNoHuman-readable caption or description
durationnumberNoDuration in seconds (audio and video)
thumbnailstringNoURL to a generated thumbnail image
tagsstringNoComma-separated tags for categorization
uploadedBy-> ContactNoPerson who uploaded the asset
sourcestringNoOriginal source or attribution
licensestringNoLicense type (e.g., CC-BY-4.0, MIT)

Relationships

FieldDirectionTargetDescription
uploadedBy->ContactThe person who uploaded this asset

Verbs

VerbEventDescription
createCreatedUpload and register a new asset
updateUpdatedUpdate asset metadata
deleteDeletedSoft-delete the asset
processProcessedTrigger post-upload processing (thumbnails, optimization, transcoding)

Verb Lifecycle

import { Asset } from '@headlessly/content'

// BEFORE hook -- validate before processing
Asset.processing(asset => {
  if (asset.type === 'Image' && asset.size > 10_000_000) {
    throw new Error('Image too large for processing (max 10MB)')
  }
})

// Execute
await Asset.process('asset_qW3mLpFd')

// AFTER hook -- update references after processing
Asset.processed((asset, $) => {
  console.log(`Asset processed: ${asset.name} (${asset.width}x${asset.height})`)
  if (asset.thumbnail) {
    $.Event.create({
      type: 'asset.thumbnail.generated',
      properties: { assetId: asset.$id, thumbnail: asset.thumbnail },
    })
  }
})

Cross-Domain Patterns

Asset is the media layer referenced across the entire content graph:

  • Content: Assets serve as featured images, inline media, and downloadable attachments for Content entities.
  • CRM: Contact avatars and organization logos can be managed assets.
  • Marketing: Campaign imagery and form attachments reference assets.
  • Analytics: Asset download and view events feed into engagement metrics.
import { Asset } from '@headlessly/content'

Asset.created((asset, $) => {
  // Auto-process images on upload
  if (asset.type === 'Image') {
    $.Asset.process(asset.$id)
  }
})

Query Examples

SDK

import { Asset } from '@headlessly/content'

// Find all images
const images = await Asset.find({ type: 'Image' })

// Get a specific asset
const asset = await Asset.get('asset_qW3mLpFd')

// Upload and register an asset
await Asset.create({
  name: 'Product Screenshot',
  filename: 'product-screenshot.webp',
  url: 'https://cdn.acme.dev/product-screenshot.webp',
  type: 'Image',
  mimeType: 'image/webp',
  size: 185_000,
  width: 1440,
  height: 900,
  alt: 'Product dashboard showing real-time metrics',
  uploadedBy: 'contact_fX9bL5nRd',
})

// Process an asset
await Asset.process('asset_qW3mLpFd')

MCP

headless.ly/mcp#search
{
  "type": "Asset",
  "filter": { "type": "Image" },
  "sort": { "$createdAt": "desc" },
  "limit": 50
}
headless.ly/mcp#fetch
{ "type": "Asset", "id": "asset_qW3mLpFd" }
headless.ly/mcp#do
const images = await $.Asset.find({ type: 'Image' })
await $.Asset.process('asset_qW3mLpFd')

REST

# List all images
curl https://headless.ly/~acme/assets?type=Image

# Get a specific asset
curl https://headless.ly/~acme/assets/asset_qW3mLpFd

# Register an asset
curl -X POST https://headless.ly/~acme/assets \
  -H 'Content-Type: application/json' \
  -d '{"name": "Product Screenshot", "filename": "screenshot.webp", "url": "https://cdn.acme.dev/screenshot.webp", "type": "Image", "mimeType": "image/webp", "size": 185000}'

# Process an asset
curl -X POST https://headless.ly/~acme/assets/asset_qW3mLpFd/process

On this page